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.mzph.cn/pingmian/19616.shtml

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

相关文章

掌握 NestJS 10.x:从零开始构建高效可扩展的服务器端应用详解

NestJS 是一个用于构建高效、可扩展的 Node.js 服务端应用的框架&#xff0c;基于 TypeScript 构建&#xff0c;并且受 Angular 的启发&#xff0c;提供了模块化、易测试、易维护的架构。NestJS 10.x 引入了一些新特性和改进&#xff0c;进一步提升了开发体验。本文将详细介绍如…

协方差和协方差矩阵是什么

协方差矩阵&#xff08;Covariance Matrix&#xff09;是一个矩阵&#xff0c;它包含多个随机变量之间的协方差。 协方差是衡量两个随机变量如何一起变化的度量。 协方差矩阵在多元统计分析和机器学习中非常重要&#xff0c;特别是在处理多元正态分布时。 详细解释 协方差&am…

生态融合促发展 YashanDB与丰图科技完成兼容性认证

近日&#xff0c;深圳计算科学研究院崖山数据库系统YashanDB V23与丰图科技智域城市数字孪生平台顺利完成兼容性互认证。经严格测试&#xff0c;双方产品完全兼容&#xff0c;稳定运行&#xff0c;充分满足企事业单位在高性能、高可用性、高稳定性及高可控性方面的核心需求&…

比较好的Python课程

最近在学习夜曲编程的Python进阶课程——办公效率化&#xff1b;夜曲编程之前有推出一款学习Python的入门课程&#xff0c;在手机端和电脑端都可以学习的&#xff0c;如果没有时间在手机端学习都很好的。每节课程学习下来&#xff0c;可以收集到Python入门的知识卡片&#xff0…

Ansys Mechanical|组装 External Mechanical Model

Assembling Finite Element Models 上文中介绍了如何导入外部模型并将其组合到单个模型中的示例。 如果要将外部模型与Workbench环境中已有的一个或多个模型组合在一起&#xff0c;该如何操作&#xff1f;本文将介绍这个工作流程。 Ansys Mechanical支持Mechanical Model和Ex…

FPGA新起点V1开发板(七-语法篇)——程序框架+高级语法(选择性做笔记)

文章目录 一、模块结构二、赋值三、条件语句 一、模块结构 默认是wire类型&#xff0c;assign是定义功能。 上面这两个always都是并行 例化 二、赋值 有两种赋值“”和“<” “”是阻塞赋值&#xff0c;也就是从上到下&#xff0c;依次完成 “”是非阻塞赋值&#xff0c;…

开展FMEA,这些准备工作你做足了吗?

在产品研发和制造过程中&#xff0c;失效模式与影响分析&#xff08;FMEA&#xff09;是一项至关重要的质量工具。它能够帮助团队提前识别和预防潜在的产品失效问题&#xff0c;从而提升产品的可靠性和质量。然而&#xff0c;要想充分发挥FMEA的效用&#xff0c;充分的准备工作…

【机器学习】Samba-CoE实现高效推理部署

Samba-CoE&#xff1a;突破AI内存墙&#xff0c;实现高效推理部署 一、引言二、Samba-CoE系统概述三、突破AI内存墙的关键技术流数据流三层内存系统 四、Samba-CoE的推理部署与优化动态模型切换资源优化分配性能加速 五、代码实例与实现细节六、结语 一、引言 随着人工智能技术…

Python爬虫实战(实战篇)—16获取【百度热搜】数据—写入Ecel(附完整代码)

文章目录 专栏导读背景结果预览1、爬取页面分析2、通过返回数据发现适合利用lxmlxpath3、继续分析【小说榜、电影榜、电视剧榜、汽车榜、游戏榜】4、完整代码总结 专栏导读 &#x1f525;&#x1f525;本文已收录于《Python基础篇爬虫》 &#x1f251;&#x1f251;本专栏专门…

AMD vs NVIDIA:2024年哪款显卡更适合你?

AMD与Nvidia之间的长期争论在显卡领域一直是一个热点话题&#xff0c;引发了游戏玩家和专业生产人士的热烈讨论。AMD和Nvidia都提供了种类丰富的 GPU&#xff0c;每款都有其优点和缺点。在本文中&#xff0c;我们将深入剖析选择AMD和Nvidia显卡时需要考虑的关键因素。 1.性能 …

【JavaEE】多线程(1)

&#x1f386;&#x1f386;&#x1f386;个人主页&#x1f386;&#x1f386;&#x1f386; &#x1f386;&#x1f386;&#x1f386;JavaEE专栏&#x1f386;&#x1f386;&#x1f386; &#x1f386;&#x1f386;&#x1f386;计算机是怎么工作的&#x1f386;&#x1f3…

neutron学习小结

概述 基于yoga版本学习neutron&#xff0c;通过源码、官方文档、部署环境进行学习 neutron-dhcp-agent neutron.agent.dhcp_agent.main 创建server&#xff0c;调oslo_service launch server&#xff0c;最后实际调了server的start方法 neutron.service.Service.start Serv…

蓝桥杯--杂题

1.贪心&#xff1a; 我们先枚举哪个国家赢了&#xff0c;然后再建一个数组排序即可。 下面是AC代码&#xff1a; #include<bits/stdc.h> using namespace std; #define int long long int n; int a[100010],b[100010],c[100010]; bool cmp(int a,int b) {return a>b…

服务器数据恢复—EqualLogic存储硬盘灯亮黄色的数据恢复案例

服务器数据恢复环境&#xff1a; 一台某品牌EqualLogic PS 6011型号存储&#xff0c;底层有一组由16块SAS硬盘组建的RAID5阵列&#xff0c;上层存储空间划分了4个卷&#xff0c;格式化为VMFS文件系统&#xff0c;存放虚拟机文件。 服务器故障&#xff1a; 存储设备上两块硬盘指…

微信小程序发送订阅消息

小程序后台。订阅消息里面&#xff0c;新建一个消息模板 小程序代码&#xff0c;登录后&#xff0c;弹出订阅信息 requestSubscribeMessage: function () {wx.requestSubscribeMessage({tmplIds: [-323232-32323], // 替换为你的模板IDsuccess(res) {// 用户订阅结果console.l…

第22讲:文件操作

文章目录 第22讲&#xff1a;文件操作1. 为什么使用文件2. 什么是文件2.1 程序文件2.2 数据文件2.3 文件名 3. 二进制文件和文本文件&#xff1f;4. 文件的打开和关闭4.1 流和标准流4.1.1 流4.1.2 标准流 4.2 文件指针4.3 文件的打开和关闭 5. 文件的顺序读写5.1 顺序读写函数介…

PyQt5开发笔记:1.环境搭建与界面美化

推荐视频教程&#xff1a; https://www.bilibili.com/video/BV1LT4y1e72X?p23&vd_source7ab611f3afb3d469faad93d3996f99ba 一、打开网址&#xff0c;点击下载 https://build-system.fman.io/qt-designer-download 下载后&#xff0c;点开exe 不推荐&#xff1a;http…

每周算法:次小生成树

题目链接 秘密的牛奶运输 题目描述 农夫约翰要把他的牛奶运输到各个销售点。 运输过程中&#xff0c;可以先把牛奶运输到一些销售点&#xff0c;再由这些销售点分别运输到其他销售点。 运输的总距离越小&#xff0c;运输的成本也就越低。 低成本的运输是农夫约翰所希望的…

react 动态form表单

需求在日常开发中反复写form 是一种低效的开发效率&#xff0c;布局而且还不同这就需要我们对其封装 为了简单明了看懂代码&#xff0c;我这里没有组件&#xff0c;都放在一起&#xff0c;简单抽离相信作为大佬的你&#xff0c;可以自己完成&#xff0c; 首先我们做动态form …

Gartner发布电信运营商应对持续变化的网络安全环境指南:现代云安全与网络安全的五大核心挑战

所有组织的云和网络都面临着高级威胁。作为网络安全的关键参与者&#xff0c;电信运营商的 CIO 需要了解行业面临的挑战&#xff0c;并了解应采用哪些解决方案来实现方法的现代化。 主要发现 电信运营商 (CSP) CIO 如果不能调整其安全策略来保护其环境&#xff0c;那么他们将会…