STM32-14-FSMC_LCD

STM32-01-认识单片机
STM32-02-基础知识
STM32-03-HAL库
STM32-04-时钟树
STM32-05-SYSTEM文件夹
STM32-06-GPIO
STM32-07-外部中断
STM32-08-串口
STM32-09-IWDG和WWDG
STM32-10-定时器
STM32-11-电容触摸按键
STM32-12-OLED模块
STM32-13-MPU

文章目录

    • 1. 显示器分类
    • 2. LCD简介
    • 3. LCD驱动原理
    • 4. LCD驱动芯片
    • 5. LCD基本驱动步骤
    • 6. FSMC介绍
        • 1. FSMC简介
        • 2. FSMC框图
        • 3. FSMC时序
        • 4. FSMC地址映射
        • 5. FSMC相关寄存器
        • 6. FSMC相关库函数
    • 7. 代码实现

1. 显示器分类

显示器举例优点缺点
断码屏数码管、计算器、遥控器成本低,驱动简单,稳定色彩单一,显示内容少
点阵屏户外广告屏任意尺寸,亮度高贵,耗电,体积大
LCD屏显示器、电视屏、手机屏成本低,色彩好,薄,寿命长全彩稍差,漏光,拖影
OLED屏显示器、电视屏、手机屏自发光,色彩最好,超薄,功耗低比较贵,寿命短

2. LCD简介

  • 简介:

    Liquid Crystal Display,即液晶显示器,利用液晶导电后透光性可变的特性,配合显示器光源、彩色滤波片和电压控制等工艺,最终可以在液晶阵列上显示彩色的图像。目前,液晶显示技术以TN、STN、TFT三种技术为主,TFT-LCD即采用了TFT技术的液晶显示器,也叫薄膜晶体管液晶显示器。

  • 优点:

    1. 低成本:低至几块钱的价格
    2. 高解析度 :高达500像素(500ppi)的分辨率,显示细腻
    3. 高对比度:高达1000:1的对比度,色彩清晰艳丽
    4. 响应速度快:高达1ms相应速度,显示效果好
  • LCD的组成:
    在这里插入图片描述

    ​ LCD(液晶显示器)的基本组成包括以下几个主要部分:

    • 玻璃基板(Glass Substrates)

      • 上玻璃基板(Top Glass Substrate): 通常带有彩色滤光片,用于产生彩色图像。
      • 下玻璃基板(Bottom Glass Substrate): 上面布有电极和TFT(薄膜晶体管)阵列,用于控制液晶分子的排列。
    • 背光源(Backlight)

      背光源提供必要的光源,通常由LED组成,光通过液晶层后形成可见图像。背光源的重要组成部分有:

      • 导光板(Light Guide Plate): 将光均匀地分布在整个显示面板上。
      • 反射板(Reflector): 反射光线,提高亮度。
      • 扩散板(Diffuser): 使光均匀分布。
    • 驱动IC

      • 驱动IC是LCD屏幕的核心部件之一,负责控制液晶分子的排列,从而控制显示内容。驱动IC通常位于玻璃基板的边缘,通过电极与液晶层相连。驱动IC接收来自显示控制器的信号,并根据这些信号来调整液晶分子的排列,以实现所需的显示效果。
  • LCD接口分类:

    接口分辨率特性
    MCU≤800*480带SRAM,无需频繁刷新,无需大内存,驱动简单
    RGB≤1280*800不带SRAM,需要实时刷新,需要大内存,驱动稍微复杂
    MIPI4K不带SRAM,支持分辨率高,省电,大部分手机屏用此接口
    1. MCU接口
    • 特点:

      • 简单控制:MCU(Microcontroller Unit)接口通常用于小尺寸、低分辨率的显示屏。控制简单,适合使用嵌入式系统进行开发。
      • 低速数据传输:MCU接口的传输速度相对较低,适合显示静态图片和简单的UI界面。
      • 常见类型80806800并行接口是常见的MCU接口类型,通常有8位、16位、18位和24位数据线。
      • 应用场景:适用于家用电器、仪表盘、小型电子设备等不需要高刷新率和高分辨率显示的场景。
    • 优缺点:

      • 优点:控制简单、成本低、容易实现和调试。
      • 缺点:传输速度慢,不适合高分辨率和动态显示需求。
    1. RGB接口
    • 特点:

      • 高速数据传输:RGB接口通过多条并行数据线传输红、绿、蓝三种颜色的数据,适合高分辨率和高速刷新显示屏。
      • 色彩表现力强:支持24-bit、18-bit等高色深传输,能显示丰富的颜色细节。
      • 应用场景:广泛应用于笔记本电脑、平板电脑、电视和高端显示器。
    • 优缺点:

      • 优点:高传输速度、色彩丰富、适合动态视频和高分辨率图像。
      • 缺点:数据线多,连接复杂,布线成本高,对电磁干扰较敏感。
    1. MIPI接口
    • 特点:

      • 高带宽低功耗:MIPI(Mobile Industry Processor Interface) DSI(Display Serial Interface)是为移动设备设计的高带宽、低功耗接口,采用高速串行传输。
      • 差分信号传输:采用差分信号传输,具有抗干扰能力强的优点。
      • 集成度高:减少引脚数,提高数据传输效率,同时支持触摸屏控制信号的传输。
      • 应用场景:广泛应用于智能手机、平板电脑、可穿戴设备等便携式电子设备。
    • 优缺点:

      • 优点:传输速度快、功耗低、抗干扰能力强、引脚数少,适合高分辨率和动态显示需求。
      • 缺点:设计和实现较复杂,对硬件设计和信号完整性要求高。

      总结:

      • MCU接口适合低分辨率、低刷新率的显示应用,控制简单、成本低,但传输速度慢。
      • RGB接口适合高分辨率、高刷新率的显示应用,传输速度快、色彩表现力强,但布线复杂、成本高。
      • MIPI接口则适合移动设备和便携式电子设备,具有高带宽、低功耗和强抗干扰能力的特点,但实现较复杂。
  • ILI9341驱动芯片:
    在这里插入图片描述

    1. MCU接口:可以使用8、9、16或18位接口进行通信;
    2. SPI接口:可以使用3线或4线SPI接口进行通信;
    3. RGB接口:可以使用6、16或18位接口进行通信。
  • 三基色原理:
    在这里插入图片描述

    在计算机中,颜色通常使用32位的格式表示,称为ARGB888格式,其中A表示Alpha通道,R、G、B分别表示红、绿、蓝三个颜色通道。在单片机中,由于资源限制,颜色通常以16位或24位表示,分别称为RGB565和RGB888格式,其中RGB565使用16位来表示颜色,而RGB888使用24位来表示颜色。

3. LCD驱动原理

  • LCD驱动原理:

    LCD屏(MCU接口)驱动的核心是:驱动LCD驱动芯片
    在这里插入图片描述

  • LCD驱动过程:
    在这里插入图片描述

  • 8080时序:

    信号名称控制状态作用
    CS片选低电平选中器件,低电平有效,先选中,后操作
    WR写信号,上升沿有效,用于数据/命令写入
    RD读信号,上升沿有效,用于数据/命令读取
    RS数据/命令0=命/1=数表示当前是读写数据还是命令,也叫DC信号
    D[15:0]数据线双向数据线,可以写入/读取驱动IC数据
    • 写时序
      在这里插入图片描述

      void lcd_wr_data(uint16_t data)
      {LCD_RS(1);		/* 操作数据 */LCD_CS(0);		/* 选中 */LCD_DATA_OUT(data);	/* 数据 */LCD_WR(0);		/* WR低电平 */LCD_WR(1);		/* WR高电平 */LCD_CS(1);		/* 释放片选 */
      }
      
    • 读时序
      在这里插入图片描述

      uint16_t  lcd_rd_data(void)
      {uint16_t ram;  		/* 定义变量 */LCD_RS(1);          /* 操作数据 */LCD_CS(0);			/* 选中 */LCD_RD(0);			/* RD低电平 */ram = LCD_DATA_IN;  /* 读取数据 */LCD_RD(1);			/* RD高电平 */LCD_CS(1);			/* 释放片选 */return ram;		   /* 返回读数 */
      }
      

4. LCD驱动芯片

  • 驱动芯片用于控制LCD的各种显示功能,整体功能复杂。常见型号ILI9341/ST7789等 。

    一般我们只需要6条指令即可完成对LCD的基本使用

    指令(HEX)名称作用
    0XD3读ID用于读取LCD控制器的ID,区分型号用
    0X36访问控制设置GRAM读写方向,控制显示方向
    0X2A列地址一般用于设置X坐标
    0X2B页地址一般用于设置Y坐标
    0X2C写GRAM用于往LCD写GRAM数据
    0X2E读GRAM用于读取LCD的GRAM数据
    • 读ID指令
      在这里插入图片描述

      具体步骤:

      1. 设置RS为低电平(0),表示要发送命令;
      2. 将命令0xD3(读ID指令)写入数据线D[15:0];
      3. 将CS信号置为低电平,选中LCD驱动IC;
      4. 触发WR信号的上升沿,将命令写入LCD驱动IC;
      5. 将RS设置为高电平(1),表示要读取数据;
      6. 将CS信号保持低电平,继续选中LCD驱动IC;
      7. 触发RD信号的上升沿,从LCD驱动IC读取数据
      8. 读取数据线D[15:0]上的数据,这就是LCD控制器型号。
    • 访问控制指令
      在这里插入图片描述

      具体步骤:

      1. 设置RS为低电平(0),表示要发送命令;
      2. 将命令0x36(访问控制指令)写入数据线D[15:0];
      3. 将CS信号置为低电平,选中LCD驱动IC;
      4. 触发WR信号的上升沿,将命令写入LCD驱动IC;
      5. 设置RS为高电平(1),表示要写入参数数据;
      6. 将参数数据(MX, MY, MV, BGR)写入数据线D[15:0];
      7. 再次触发WR信号的上升沿,将参数数据写入LCD驱动IC;
      8. 将CS信号置为高电平,取消对LCD驱动IC的选中状态。

      在这里插入图片描述

    • X坐标设置指令
      在这里插入图片描述

      具体步骤:

      1. 设置RS为低电平(0),表示要发送命令;
      2. 将命令0x2A(X坐标设置指令)写入数据线D[15:0];
      3. 将CS信号置为低电平,选中LCD驱动IC;
      4. 触发WR信号的上升沿,将命令写入LCD驱动IC;
      5. 设置RS为高电平(1),表示要写入参数数据;
      6. 将起始坐标(SC)写入数据线D[15:0];
      7. 再次触发WR信号的上升沿,将起始坐标写入LCD驱动IC;
      8. 将结束坐标(EC)写入数据线D[15:0];
      9. 再次触发WR信号的上升沿,将结束坐标写入LCD驱动IC;
      10. 将CS信号置为高电平,取消对LCD驱动IC的选中状态。
    • Y坐标设置指令
      在这里插入图片描述

      具体步骤:

      1. 设置RS为低电平(0),表示要发送命令;
      2. 将命令0x2B(Y坐标设置指令)写入数据线D[15:0];
      3. 将CS信号置为低电平,选中LCD驱动IC;
      4. 触发WR信号的上升沿,将命令写入LCD驱动IC;
      5. 设置RS为高电平(1),表示要写入参数数据;
      6. 将起始坐标(SP)写入数据线D[15:0];
      7. 再次触发WR信号的上升沿,将起始坐标写入LCD驱动IC;
      8. 将结束坐标(EP)写入数据线D[15:0];
      9. 再次触发WR信号的上升沿,将结束坐标写入LCD驱动IC;
      10. 将CS信号置为高电平,取消对LCD驱动IC的选中状态。
    • 写GRAM指令
      在这里插入图片描述

      具体步骤:

      1. 设置RS为低电平(0),表示要发送命令;
      2. 将命令0x2C(写GRAM指令)写入数据线D[15:0];
      3. 将CS信号置为低电平,选中LCD驱动IC;
      4. 触发WR信号的上升沿,将命令写入LCD驱动IC;
      5. 设置RS为高电平(1),表示要写入数据;
      6. 准备要写入的像素点的颜色值(RGB565格式),写入数据线D[15:0];
      7. 再次触发WR信号的上升沿,将像素点的颜色值写入LCD驱动IC;
      8. 如果需要连续写入多个像素点,数据线会自动自增,无需重新设置坐标。
    • 读GRAM指令
      在这里插入图片描述

      具体步骤:

      1. 设置RS为低电平(0),表示要发送命令;
      2. 将命令0x2E(读GRAM指令)写入数据线D[15:0];
      3. 将CS信号置为低电平,选中LCD驱动IC;
      4. 触发WR信号的上升沿,将命令写入LCD驱动IC;
      5. 设置RS为高电平(1),表示要读取数据;
      6. 发送dummy数据,读取GRAM中的垃圾数据(dummy);
      7. 依次读取R1G1和B1R2数据,每次读取一个像素点的颜色;
      8. 根据RGB565格式的颜色值,提取出红色、绿色和蓝色通道的值,并返回合并后的颜色值。

      在这个过程中,需要连续读取3次数据,每次读取一个像素点的颜色值。其中,dummy数据用于丢弃GRAM中的垃圾数据,实际上不会被使用。而读取R1G1和B1R2数据则是实际的颜色值数据,需要根据RGB565格式进行处理,提取出红色、绿色和蓝色通道的值,最终合并成完整的颜色值。

      读取某个点颜色函数代码:

      uint16_t lcd_rd_data(void)
      {uint16_t ram;  		/* 定义变量 */DATA_IN_MODE();	    /* 设置数据输入 */LCD_RS(1);          /* 操作数据 */LCD_CS(0);			/* 选中 */LCD_RD(0);		    /* RD低电平 */ram = LCD_DATA_IN;  /* 读取数据 */LCD_RD(1);		    /* RD高电平 */LCD_CS(1);			/* 释放片选 */DATA_OUT_MODE();	/* 设置数据输出 */return ram;		   /* 返回读数 */
      }uint16_t lcd_read_point(uint16_t x, uint16_t y)
      {uint16_t r = 0, g = 0, b = 0;	/* 定义变量 */lcd_set_cursor(x, y);		/* 设置坐标 */lcd_wr_regno(0X2E);		    /* 发读点命令 */r = lcd_rd_data();  		/* 假读 */r = lcd_rd_data();  		/* 读rg */b = lcd_rd_data(); 		    /* 读b */g = r & 0XFF;       	    /* 得到g值 */return (((r >> 11) << 11) | ((g >> 2) << 5) | (b >> 11));
      }
      

      将红色、绿色和蓝色分量组合成一个16位的颜色值并返回:

      • 红色分量:将 r 右移11位,获得红色分量的高5位,再左移11位放到返回值的高5位。
      • 绿色分量:将 g 右移2位,获得绿色分量的高6位,再左移5位放到返回值的中6位。
      • 蓝色分量:将 b 右移11位,获得蓝色分量的高5位,直接放到返回值的低5位。

      这段代码通过指定坐标,发送读取命令,读取并组合红、绿、蓝三种颜色的分量,最后返回组合后的16位颜色值。每个颜色分量都经过适当的移位和合并操作,以符合16位颜色格式。

5. LCD基本驱动步骤

  • LCD驱动的一般过程

    1. 电源初始化

      • 打开LCD电源并进行复位。
      • 配置电源电压、时钟等参数。
    2. 硬件接口初始化

      • 根据LCD的接口类型(MCU接口、RGB接口、MIPI接口等)配置MCU的GPIO引脚和通信协议。
        在这里插入图片描述
    3. 寄存器配置

      • 通过写入特定命令到LCD控制器寄存器来配置显示参数,例如分辨率、颜色格式、扫描方向等。
    4. 内存映射

      • 将LCD的显示内存映射到MCU的地址空间,方便后续的读写操作。
    5. 显示测试

      • 进行显示测试,例如填充屏幕、显示图像等,确保LCD初始化成功。

    8080底层操作函数

    8080接口是一种并行通信接口,常用于LCD与MCU之间的数据传输。8080底层操作函数:

    /* 8080 写数据 */
    void lcd_wr_data (uint16_t data)
    {LCD_RS(1);          /* 操作数据 */LCD_CS(0);          /* 选中 */LCD_DATA_OUT(data); /* 数据 */LCD_WR(0);          /* WR低电平 */LCD_WR(1);          /* WR高电平 */LCD_CS(1);          /* 释放片选 */
    }/* 8080 写命令 */
    void lcd_wr_regno(uint16_t regno)
    {LCD_RS(0);          /* RS=0,表示写寄存器 */LCD_CS(0);          /* 选中 */LCD_DATA_OUT(regno);/* 命令 */LCD_WR(0);          /* WR低电平 */LCD_WR(1);          /* WR高电平 */LCD_CS(1);          /* 释放片选 */
    }
    

    初始化LCD

    初始化LCD的过程包括设置电源、配置寄存器和初始化显示内存等步骤。

    /* 初始化LCD */
    void lcd_init(void)
    {GPIO_InitTypeDef gpio_init_struct;LCD_BL_GPIO_CLK_ENABLE();   /* LCD_BL脚时钟使能 */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_DATA_GPIO_CLK_ENABLE(); /* LCD_DATA脚时钟使能 */__HAL_RCC_AFIO_CLK_ENABLE();__HAL_AFIO_REMAP_SWJ_NOJTAG(); /* 禁止JTAG, 使能SWD, 释放PB3,PB4两个引脚做普通IO用 */gpio_init_struct.Pin = LCD_BL_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽复用 */gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */HAL_GPIO_Init(LCD_BL_GPIO_PORT, &gpio_init_struct);     /* LCD_BL引脚模式设置(推挽输出) */gpio_init_struct.Pin = LCD_CS_GPIO_PIN;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_DATA_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */HAL_GPIO_Init(LCD_DATA_GPIO_PORT, &gpio_init_struct);   /* LCD_DATA引脚模式设置 */LCD_WR(1);                  /* WR 默认高电平 */LCD_RD(1);                  /* RD 默认高电平 */LCD_CS(1);                  /* CS 默认高电平 */LCD_RS(1);                  /* RS 默认高电平 */LCD_DATA_OUT(0XFFFF);       /* DATA 默认高电平 *//* 读取ID */lcd_wr_regno(0xD3);lcddev.id = lcd_rd_data();  /* 假读 */lcddev.id = lcd_rd_data();  /* 00 */lcddev.id = lcd_rd_data();  /* 93 */lcddev.id <<= 8;lcddev.id |= lcd_rd_data();  /* 41 */printf("lcddev_id:%#x \r\n", lcddev.id);/* 完成初始化序列 */if (lcddev.id == 0x9341)lcd_ex_ili9341_reginit();elselcd_ex_st7789_reginit();/* 对LCD控制结构体赋值 */lcddev.width = 240;lcddev.height = 320;lcddev.setxcmd = 0x2A;lcddev.setycmd = 0x2B;lcddev.wramcmd = 0x2C;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);/* 设置扫描方向 */lcd_write_reg(0x36, 1 << 3);/* 点亮背光 */LCD_BL(1);/* lcd_clear */lcd_clear(0xFFFF);
    }
    

    实现画点函数

    画点函数用于在指定位置显示一个像素点。

    /* 画点 */
    void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color)
    {lcd_set_cursor(x, y);lcd_write_ram_prepare();lcd_wr_data(color);
    }
    

    实现读点函数

    读点函数用于读取指定位置的像素点颜色值。

    /* LCD读数据 */
    uint16_t lcd_rd_data(void)
    {volatile uint16_t ram;  /* 防止被优化 */GPIO_InitTypeDef gpio_init_struct;/* LCD_DATA 引脚模式设置, 上拉输入, 准备接收数据 */gpio_init_struct.Pin = LCD_DATA_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_INPUT;gpio_init_struct.Pull = GPIO_PULLUP;gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(LCD_DATA_GPIO_PORT, &gpio_init_struct); LCD_RS(1);              /* RS=1,表示操作数据 */LCD_CS(0);LCD_RD(0);lcd_opt_delay(2);ram = LCD_DATA_IN;      /* 读取数据 */LCD_RD(1);LCD_CS(1);/* LCD_DATA 引脚模式设置, 推挽输出, 恢复输出状态 */gpio_init_struct.Pin = LCD_DATA_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;gpio_init_struct.Pull = GPIO_PULLUP;gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(LCD_DATA_GPIO_PORT, &gpio_init_struct);   return ram;
    }/* 读点 */
    uint16_t  lcd_read_point (uint16_t x, uint16_t y)
    {uint16_t r = 0, g = 0, b = 0;   /* 定义变量 */lcd_set_cursor(x, y);           /* 设置坐标 */lcd_wr_regno(0X2E);             /* 发读点命令 */r = lcd_rd_data();              /* 假读 */r = lcd_rd_data();              /* 读rg */b = lcd_rd_data();              /* 读b */g = r & 0XFF;                   /* 得到g值 */return (((r >> 11) << 11) | ((g >> 2) << 5) | (b >> 11));
    }
    

6. FSMC介绍

1. FSMC简介
  • Flexible Static Memory Controller灵活的静态存储控制器
  • 用途: 用于驱动SRAM,NOR FLASH,NAND FLASH及PC卡类型的存储器。 它的主要作用是简化对这些存储器的访问,通过配置FSMC外设,可以直接使用指针操作来修改存储单元的内容,而不需要程序员去实现复杂的时序控制。
  • 配置好FSMC后,可以定义一个指针指向这些存储器的地址,然后通过对指针的操作来读写存储单元的内容。FSMC会自动完成读写命令和数据访问操作,不需要程序员去关心时序控制的细节。
  • FSMC外设配置好就可以模拟出时序。F1/ F4(407)系列大容量型号,且引脚数目在100脚以上的芯片都有FSMC接口,F4/F7/H7系列就是FMC接口。
2. FSMC框图

在这里插入图片描述

  1. 时钟逻辑控制

    时钟控制逻辑负责生成和管理FSMC操作所需的时钟信号。这些时钟信号用于同步FSMC内部操作以及与外部设备的通信。主要功能包括:

    • 时钟源选择:FSMC可以从系统时钟或其他预定时钟源获取时钟信号。时钟源的选择可能通过配置寄存器进行设置。
    • 时钟分频:FSMC内部可以对输入时钟信号进行分频,以生成适合不同操作速度的时钟。分频比通过特定的配置寄存器设置。
    • 时钟启停控制:根据需要,可以启动或停止FSMC的时钟,以节省功耗。时钟控制逻辑负责在需要时准确地启动或停止时钟信号。
  2. 控制单元

    FSMC控制单元是FSMC的核心部分,负责管理与外部存储器的交互。主要功能包括:

    • 地址映射:将处理器生成的虚拟地址映射到外部存储器的物理地址。这涉及地址总线和片选信号的生成和管理。
    • 读写控制:生成读写操作的控制信号(如读使能、写使能)。这些信号用于协调处理器与外部存储器之间的数据传输。
    • 时序控制:FSMC控制单元负责控制读写操作的时序,以确保与外部存储器的通信符合时序要求。这包括设置和保持时间的控制。
    • 数据总线管理:管理数据总线的方向和数据传输。在读操作时,将外部存储器的数据传送到处理器;在写操作时,将处理器的数据传送到外部存储器。
    • 错误检测与纠正:在数据传输过程中进行错误检测,并在可能的情况下进行纠正,以确保数据完整性。
  3. 通信引脚

    通信引脚是FSMC与外部设备(如SRAM、NOR Flash、NAND Flash等)进行实际物理连接的接口。主要引脚包括:
    在这里插入图片描述

  • 使用FSMC驱动LCD
    在这里插入图片描述

    右边是FSMC控制屏幕的过程,使用的是HADDR总线, CPU通过HADDR总线将控制信息发送给FSMC,然后FSMC再控制SRAM。 8080总线是CPU直接控制屏幕的方式,而HADDR总线是通过配置FSMC来控制屏幕的方式 。

3. FSMC时序

在这里插入图片描述

  • 模式1
    • 这种模式适用于 SRAM/CRAM 存储器,其时序特性如下:
      • 在读操作时,输出使能(OE)在读时序片选过程不翻转。
      • 存在 NBL(Narrow Bus Low)信号,用于指示总线宽度。
      • 不存在 NADV(NAND Address Valid)信号。
  • 拓展模式
    • 拓展模式相对于模式1来说,读写时序时间参数设置可以不同,以满足存储器读写时序不一样的需求。
    • 拓展模式包括了模式A、模式B/2、模式C 和模式D,针对不同类型的存储器有不同的时序特性:
      • 模式A:适用于 SRAM/PSRAM(CRAM)存储器,与模式1类似,但是在读操作时,输出使能(OE)在读时序片选过程翻转。
      • 模式B/2:适用于 NOR FLASH 存储器,读操作时输出使能(OE)在读时序片选过程不翻转,不存在 NBL 信号,但有 NADV 信号。
      • 模式C:也适用于 NOR FLASH 存储器,读操作时输出使能(OE)在读时序片选过程翻转,不存在 NBL 信号,但有 NADV 信号。
      • 模式D:适用于带地址扩展的异步操作,读操作时输出使能(OE)在读时序片选过程翻转,不存在 NBL 信号,有 NADV 信号,且存在地址保存时间。
        在这里插入图片描述
4. FSMC地址映射

在这里插入图片描述

针对FSMC存储块划分,可以进一步解释如下:

  1. 外部存储器划分为四个存储块:这意味着STM32可以与四个不同的外部存储器设备通信,每个设备都具有独立的存储空间。
  2. 每个存储块大小为256M字节:每个存储块都有256M字节的存储空间,可以容纳大量的数据。
  3. FSMC存储块1划分为四个区:存储块1是FSMC的一个特定区域,它被分成了四个区域,每个区域管理64M字节的空间。这种划分方式可以帮助对存储器进行更灵活的管理和组织。
  • HADDR与FSMC_A关系
    在这里插入图片描述

  • LCD的RS信号线与地址线关系
    在这里插入图片描述

5. FSMC相关寄存器
  • FSMC_BCR4FSMC_BTR4FSMC_BWTR4寄存器
    在这里插入图片描述

  • FSMC_BCRx寄存器
    在这里插入图片描述

  • FSMC_BTRx寄存器
    在这里插入图片描述

  • FSMC_BWTRx寄存器
    在这里插入图片描述
    在这里插入图片描述

6. FSMC相关库函数
HAL_StatusTypeDef HAL_SRAM_Init ( SRAM_HandleTypeDef *hsram, FSMC_NORSRAM_TimingTypeDef *Timing, FSMC_NORSRAM_TimingTypeDef *ExtTiming )

参数:

  1. SRAM_HandleTypeDef *hsram:这是指向 SRAM 句柄结构的指针。该结构包含了 SRAM 模块的配置信息,如 SRAM 的基地址、数据宽度、存储器库编号等。
  2. FSMC_NORSRAM_TimingTypeDef *Timing:这是指向 SRAM 读写访问时序结构的指针。FSMC_NORSRAM_TimingTypeDef 结构定义了时序参数,如地址设置时间、数据保持时间、总线转换时间、时钟分频、数据延迟和访问模式等。
  3. FSMC_NORSRAM_TimingTypeDef *ExtTiming:这是指向扩展模式时序结构的指针(如果使用的话)。扩展模式允许在某些操作需要不同的时序时应用额外的时序配置。

功能:

HAL_SRAM_Init 函数通过配置 FSMC 接口中的时序参数来初始化 SRAM 设备。具体步骤如下:

  1. 句柄初始化:函数首先初始化 SRAM 句柄 (hsram),包括设置基地址和其他配置参数。
  2. 时序配置:设置 FSMC 的读写时序参数,这包括:
    • 地址设置时间:在片选之前设置地址的时间。
    • 数据保持时间:在操作完成之前保持数据的时间。
    • 访问模式:如异步模式。
  3. FSMC 配置:配置 FSMC 外设的时序参数,具体包括:
    • 设置 FSMC 控制寄存器,定义存储器类型、数据宽度、存储器库和读写时序参数。
    • 配置控制信号,如片选、输出使能和写入使能线。
  4. 扩展模式(可选):如果提供了 ExtTiming 参数(非空),函数将配置扩展模式时序参数。扩展模式允许对某些操作使用不同的读写时序。
  5. 启用 FSMC:一旦设置了时序配置,启用 FSMC 控制器开始与 SRAM 通信。

7. 代码实现

  • LCD初始化函数

    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;          /* 高速 */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 = 0;      /* 地址建立时间(ADDSET)为1个HCLK 1/72M = 13.9ns (实际 > 200ns) */fsmc_read_handle.AddressHoldTime = 0;       /* 地址保持时间(ADDHLD) 模式A是没有用到 *//* 因为液晶驱动IC的读数据的时候,速度不能太快,尤其是个别奇葩芯片 */fsmc_read_handle.DataSetupTime = 15;        /* 数据保存时间(DATAST)为16个HCLK = 13.9 * 16 = 222.4ns */fsmc_read_handle.AccessMode = FSMC_ACCESS_MODE_A;    /* 模式A *//* FSMC写时序控制寄存器 */fsmc_write_handle.AddressSetupTime = 0;     /* 地址建立时间(ADDSET)为1个HCLK = 13.9ns */fsmc_write_handle.AddressHoldTime = 0;      /* 地址保持时间(ADDHLD) 模式A是没有用到 *//* 某些液晶驱动IC的写信号脉宽,最少也得50ns */fsmc_write_handle.DataSetupTime = 1;        /* 数据保存时间(DATAST)为2个HCLK = 13.9 * 2 = 27.8ns (实际 > 200ns) */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();  /* 读取0x93 */lcddev.id <<= 8;lcddev.id |= lcd_rd_data(); /* 读取0x41 */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,尝试看看是不是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高8位 */lcddev.id = lcd_rd_data();      /* 读回0X55 */lcddev.id <<= 8;lcd_wr_regno(0xC501);           /* 读取ID低8位 */lcddev.id |= lcd_rd_data();     /* 读回0X10 */delay_ms(5);if (lcddev.id != 0X5510)        /* 也不是NT5510,尝试看看是不是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 == 0x5510){lcd_ex_nt35510_reginit();   /* 执行NT35510初始化 */}else if (lcddev.id == 0X1963){lcd_ex_ssd1963_reginit();   /* 执行SSD1963初始化 */lcd_ssd_backlight_set(100); /* 背光设置为最亮 */}lcd_display_dir(0); /* 默认为竖屏 */LCD_BL(1);          /* 点亮背光 */lcd_clear(WHITE);
    }
    
  • 主函数

    int main(void)
    {uint8_t lcd_id[12];HAL_Init();                                         /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9);                 /* 设置时钟, 72Mhz */delay_init(72);                                     /* 延时初始化 */usart_init(115200);                                 /* 串口初始化为115200 */led_init();                                         /* 初始化LED */oled_init();lcd_init();                                         /* 初始化LCD */sprintf((char *)lcd_id, "LCD ID:%04X", lcddev.id);  /* 将LCD ID打印到lcd_id数组 */while (1){lcd_show_string(10, 40, 240, 32, 32, "STM32", GREEN);lcd_show_string(10, 80, 240, 24, 24, "TFTLCD TEST", BLUE);lcd_show_string(10, 110, 240, 16, 16, "ATOM@ALIENTEK", RED);lcd_show_string(10, 130, 240, 16, 12, (char *)lcd_id, RED); /* 显示LCD ID */oled_show_string(0, 5, "STM32", 24);oled_show_string(0, 33, "TFTLCD TEST", 16);oled_show_string(0, 51, "ATOM@ALIENTEK", 12);oled_refresh_gram(); LED0_TOGGLE(); /*红灯闪烁*/delay_ms(1000);}
    }
    
  • 实验结果
    在这里插入图片描述

声明:资料来源(战舰STM32F103ZET6开发板资源包)

  1. Cortex-M3权威指南(中文).pdf
  2. STM32F10xxx参考手册_V10(中文版).pdf
  3. STM32F103 战舰开发指南V1.3.pdf
  4. STM32F103ZET6(中文版).pdf
  5. 战舰V4 硬件参考手册_V1.0.pdf

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

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

相关文章

[Windows] 植物大战僵尸杂交版

游戏包含冒险模式、挑战模式、生存模式三种不同玩法。冒险模式主打关卡闯关&#xff0c;挑战模式则挑战特殊设计的关卡&#xff0c;生存模式结合无尽模式和特殊地图&#xff0c;各具特色。玩家可根据喜好自由选择模式&#xff0c;体验不同的游戏乐趣。快来尝试这款独特的pvz游戏…

6月2(信息差)

&#x1f30d;特斯拉&#xff1a;Model3高性能版预计6月中旬开启首批交付 &#x1f384;微软对开源字体 Cascadia Code 进行重大更新 ✨天猫618加码引爆消费热潮 截至晚9点185个品牌成交破亿 1.瑞士清洁科技公司Librec开发废旧锂离子电池回收技术&#xff0c;可回收电池90%的…

【设计模式】JAVA Design Patterns——Factory Method(虚拟构造器模式)

&#x1f50d;目的 为创建一个对象定义一个接口&#xff0c;但是让子类决定实例化哪个类。工厂方法允许类将实例化延迟到子类 &#x1f50d;解释 真实世界例子 铁匠生产武器。精灵需要精灵武器&#xff0c;而兽人需要兽人武器。根据客户来召唤正确类型的铁匠。 通俗描述 它为类…

IDEA2020.3部署旧的的web工程,报错,参考下面的配置

以下内容&#xff0c;需要仔细核对&#xff0c;有些配置只是针对本项目进行的配置&#xff0c;仅供参考&#xff0c;可以解决一些问题。 File->Project Structure&#xff1a; Tomcat配置&#xff1a; 完成。 详细内容&#xff0c;参考&#xff1a;IDEA2020.3部署旧的的web工…

备战十一届大唐杯国赛预选赛

这次省赛带了太多个省一了&#xff0c;具体可看下面的图片&#xff0c;只放了一部分。目前根据可靠消息&#xff0c;应该还有个预选赛和去年一样&#xff0c;就是还会考一次仿真。如果说通过了就是国二起步然后去北方工业争夺国一国二&#xff0c;没过的话就是国三。 每…

Python实用代码片段分享(三)

在今天的博文中&#xff0c;我们将继续分享一些Python编程中非常实用的代码片段。这些代码片段将帮助你更高效地处理常见任务&#xff0c;从字符转换到数据类型检查&#xff0c;应有尽有。 1. ord函数和chr函数 Python的ord()函数可以返回Unicode字符对应的ASCII码值&#xf…

精准检测,可燃气体报警系统的技术原理与特点

在现代化的工业生产与日常生活中&#xff0c;可燃气体泄露事故频发&#xff0c;给人们的生命和财产安全带来了严重威胁。 因此&#xff0c;可燃气体报警检测系统的应用变得尤为重要。它不仅能够实时监测环境中的可燃气体浓度&#xff0c;还能在发现异常情况时及时报警&#xf…

[leetcode hot150]第五十七题,插入区间

题目&#xff1a; 给你一个 无重叠的 &#xff0c;按照区间起始端点排序的区间列表 intervals&#xff0c;其中 intervals[i] [starti, endi] 表示第 i 个区间的开始和结束&#xff0c;并且 intervals 按照 starti 升序排列。同样给定一个区间 newInterval [start, end] 表示…

【机器学习】——驱动智能制造的青春力量,优化生产、预见故障、提升质量

目录 一.优化生产流程 1.1 数据收集 1.2 数据预处理 1.3 模型训练 1.4 优化建议 1.5 示例代码 二.预测设备故障 2.1 数据收集 2.2 数据预处理 2.3 模型训练 2.4 故障预测 2.5 示例代码 三.提升产品质量 3.1 数据收集 3.2 数据预处理 3.3 模型训练 3.4 质量提升…

最新一站式AI创作中文系统网站源码+系统部署+支持GPT对话、Midjourney绘画、Suno音乐、GPT-4o文档分析等大模型

一、系统简介 本文将介绍最新的一站式AI创作中文系统&#xff08;集成ChatGPTMidjourneySunoStable Diffusion&#xff09;——星河易创AI系统&#xff0c;该系统基于ChatGPT的核心技术&#xff0c;融合了自然语言问答、绘画、音乐、文档分享、图片识别等创作功能&#xff0c;…

VTK9.3.0刻度标签重叠的问题

本文采用VTK9.3.0版本&#xff0c;其他版本如VKT8.0亦有同样的问题 VTK显示文本时&#xff0c;Z轴刻度标签出现了重叠&#xff0c;如下图&#xff1a; 寻找好久&#xff0c;没有找到设置标签间距、个数等相关的公有成员函数&#xff0c;此问题一直没有解决。 于是想到改VKT9.…

重生之 SpringBoot3 入门保姆级学习(10、日志基础与使用)

重生之 SpringBoot3 入门保姆级学习&#xff08;10、日志基础使用&#xff09; 3.1 日志基础3.2 使用日志3.2.1 基础使用3.2.2 调整日志级别3.2.3 带参数的日志 3.1 日志基础 SpringBoot 默认使用 SLF4j&#xff08;Simple Logging Facade for Java&#xff09;和 Logback 实现…

2024089期传足14场胜负前瞻

2024089期售止时间为6月3日&#xff08;周一&#xff09;22点00分&#xff0c;敬请留意&#xff1a; 本期1.5以下赔率5场&#xff0c;1.5-2.0赔率5场&#xff0c;其他场次是平半盘、平盘。本期14场难度偏低。以下为基础盘前瞻&#xff0c;大家可根据自身判断&#xff0c;复选增…

pycharm简易使用码云gitee

文章目录 参考文献官网地址安装插件第一个选项报错了不可&#xff0c;第二个选项&#xff0c;可以了新库上传到主分支&#xff0c;push改进实验新建分支&#xff0c;上传为新分支&#xff1a;做另一种改进&#xff0c;选择回退主分支&#xff0c;另建一个分支 使用对于一个新项…

【stm32/CubeMX、HAL库】swjtu嵌入式实验七 ADC 实验

相关电路与IO引脚 注意&#xff1a;串口打印重定向后使用printf打印需要在keil里勾选 Use MicroLIB &#xff0c;否则会卡住。 参看&#xff1a;https://zhuanlan.zhihu.com/p/565613666 串口重定向&#xff1a; /* USER CODE BEGIN Includes */#include <stdio.h>//…

(函数)颠倒字符串顺序(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h> # include <string.h>//声明颠倒函数; void reverse(char a[]) {//初始化变量值&#xff1b;int i, j;char t;//循环颠倒&#xff1b;for (i 0, j strl…

寄存器、缓存、内存(虚拟、物理地址)、DDR、RAM的关系

寄存器、缓存、内存、DDR、RAM的关系 1. 主要概念内部存储器&#xff1a;2.1 寄存器&#xff0c;register2.2 主存储器&#xff0c;内存&#xff0c;memory2.3 缓存&#xff0c;高速缓冲存储器&#xff0c;cache 外部存储器2.4 快闪存储器&#xff0c;闪存&#xff0c;flash Me…

Android 使用kotlin Retrofit2 + Dagger2完成网络请求跟依赖注入组合使用

文章目录 &#xff08;一&#xff09;引入依赖&#xff08;二&#xff09;基本概念Dagger中的基本概念&#xff1a;Retrofit介绍 &#xff08;三&#xff09;Dagger2 Module 和 Provides 和 Component Inject&#xff08;四&#xff09;Retrofit2 创建数据类Bean跟Service服务&…

wpf listbox实现选中动画

效果如下&#xff1a; 参考&#xff1a;https://github.com/WPFDevelopersOrg/WPFDevelopers/blob/master/src/WPFDevelopers.Samples.Shared/Controls/NavigateMenu/NavigateMenu.xaml 实现上述效果的前台代码&#xff1a; <Windowx:Class"ListBox.MainWindow"…

用户管理的小demo--查询

目录 1、先在数据库中 添加数据 2、servlet层 2.1 在servlet中 找到上一模块“LoginServlet.java”&#xff0c;在里面添加代码 2.2 在servlet层 new SelectUserServlet类 2.3 在web.xml 中&#xff0c;新增配置 3、service 层 4、 dao层 4.1 UserDao 4.2 UserDaoImpl…