• [问题求助] 寻农业物联网平台嵌入式开发
    寻农业物联网平台嵌入式开发更新
  • [技术干货] STM32入门开发 介绍SPI总线、读写W25Q64(FLASH)(硬件+模拟时序)
    # 一、环境介绍 **编程软件:** keil5 **操作系统:** win10 **MCU型号:** STM32F103ZET6 **STM32编程方式:** 寄存器开发 (方便程序移植到其他单片机) **SPI总线:** STM32本身支持SPI硬件时序,本文示例代码里同时采用模拟时序和硬件时序两种方式读写W25Q64。 模拟时序更加方便移植到其他单片机,更加方便学习理解SPI时序,通用性更高,不分MCU; 硬件时序效率更高,每个MCU配置方法不同,依赖MCU硬件本身支持。 **存储器件:** 采用华邦W25Q64 flash存储芯片。 W25Q64这类似的Flash存储芯片在单片机里、嵌入式系统里还是比较常见,可以用来存储图片数据、字库数据、音频数据、保存设备运行日志文件等。 # 二、华邦W25Q64介绍(FLASH存储类型) ## 2.1 W25Q64芯片功能介绍 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659491234258425773.png) W25Q64是为系统提供一个最小空间、最少引脚,最低功耗的串行Flash存储器,25Q系列比普通的串行Flash存储器更灵活,性能更优越。 W25Q64支持双倍/四倍的SPI,可以储存包括声音、文本、图片和其他数据;芯片支持的工作电压 2.7V 到 3.6V,正常工作时电流小于5mA,掉电时低于1uA,所有芯片提供标准的封装。 W25Q64的内存空间结构: 一页256字节,4K(4096 字节)为一个扇区,16个扇区为1块,容量为8M字节,共有128个块,2048 个扇区。 W25Q64每页大小由256字节组成,每页的256字节用一次页编程指令即可完成。 擦除指令分别支持: 16页(1个扇区)、128页、256页、全片擦除。 W25Q64支持标准串行外围接口(SPI),和高速的双倍/四倍输出,双倍/四倍用的引脚:串行时钟、片选端、串行数据 I/O0(DI)、I/O1(DO)、I/O2(WP)和 I/O3(HOLD)。 SPI 最高支持 80MHz,当用快读双倍/四倍指令时,相当于双倍输出时最高速率160MHz,四倍输出时最高速率 320MHz。这个传输速率比得上8位和16位的并行Flash存储器。 W25Q64支持 JEDEC 标准,具有唯一的 64 位识别序列号,方便区别芯片型号。 ## 2.2 W25Q64芯片特性详细介绍 **●SPI串行存储器系列** -W25Q64:64M 位/8M 字节 -W25Q16:16M 位/2M 字节 -W25Q32:32M 位/4M 字节 -每 256 字节可编程页 ## 2.3 引脚介绍 下面只介绍W25Q64标准SPI接口,因为目前开发板上的封装使用的就是标准SPI接口。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659491248542222859.png) | **引脚编号** | **引脚名称** | **I/O** | **功能** | | ------------ | ------------ | ------- | ----------------------------------- | | **1** | **/CS** | **I** | **片选端输入** | | **2** | **DO(IO1)** | **I/O** | **数据输出(数据输入输出 1)\*1** | | **3** | **/WP(IO2)** | **I/O** | **写保护输入(数据输入输出 2)\*2** | | **4** | **GND** | | **地** | | **5** | **DI(IO0)** | **I/O** | **数据输入(数据输入输出 0)\*1** | | **6** | **CLK** | **I** | **串行时钟输入** | | ----- | -------------- | ------- | ----------------------------------- | | **7** | **/HOLD(IO3)** | **I/O** | **保持端输入(数据输入输出 3)\*2** | | **8** | **VCC** | | **电源** | ### **2.2.1 SPI片选(/CS)引脚用于使能和禁止芯片操作** CS引脚是W25Q64的片选引脚,用于选中芯片;当CS为高电平时,芯片未被选择,串行数据输出(DO、IO0、IO1、IO2 和 IO3)引脚为高阻态。未被选择时,芯片处于待机状态下的低功耗,除非芯片内部在擦除、编程。当/CS 变成低电平,芯片功耗将增长到正常工作,能够从芯片读写数据。上电后, 在接收新的指令前,/CS 必须由高变为低电平。上电后,/CS 必须上升到 VCC,在/CS 接上拉电阻可以完成这个操作。 ### 2.2.2 串行数据输入、输出和 IOs(DI、DO 和 IO0、IO1、IO2、IO3) W25Q64、W25Q16 和 W25Q32 支持标准 SPI、双倍 SPI 和四倍 SPI。 标准的 SPI 传输用单向的 DI(输入)引脚连续的写命令、地址或者数据在串行时钟(CLK)的上升沿时写入到芯片内。 标准的SPI 用单向的 DO(输出)在 CLK 的下降沿从芯片内读出数据或状态。 ### 2.2.3 写保护(/WP) 写保护引脚(/WP)用来保护状态寄存器。和状态寄存器的块保护位(SEC、TB、BP2、BP1 和BP0)和状态寄存器保护位(SRP)对存储器进行一部分或者全部的硬件保护。/WP 引脚低电平有效。当状态寄存器 2 的 QE 位被置位了,/WP 引脚(硬件写保护)的功能不可用。 ### 2.2.4 保持端(/HOLD) 当/HOLD 引脚是有效时,允许芯片暂停工作。在/CS 为低电平时,当/HOLD 变为低电平,DO 引脚将变为高阻态,在 DI 和 CLK 引脚上的信号将无效。当/HOLD 变为高电平,芯片恢复工作。/HOLD 功能用在当有多个设备共享同一 SPI 总线时。/HOLD 引脚低电平有效。当状态寄存器 2 的 QE 位被置位了,/ HOLD 引脚的功能不可用。 ### 2.2.5 串行时钟(CLK) 串行时钟输入引脚为串行输入和输出操作提供时序。(见 SPI 操作)。 设备数据传输是从高位开始,数据传输的格式为 8bit,数据采样从第二个时间边沿开始,空闲状态时,时钟线 clk 为高电平。 ## 2.3 内部结构框架图 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659491308969766341.png) ## 2.4 W25Q64的标准SPI操作流程 W25Q64标准SPI总线接口包含四个信号: **串行时钟(CLK)、片选端(/CS)、串行数据输入(DI)和串行数据输出(DO)**。 DI输入引脚在CLK的上升沿连续写命令、地址或数据到芯片内。 DO输出引脚在CLK的下降沿从芯片内读出数据或状态。 W25Q64分别支持SPI总线工作模式0和工作模式3。模式0和模式3的主要区别在于常态时的CLK信号不同;对于模式0来说,当SPI主机已准备好数据还没传输到串行Flash中时,CLK信号常态为低; **设备数据传输是从高位开始,数据传输的格式为8bit,数据采样从第二个时间边沿开始,空闲状态时,时钟线clk为高电平**。 ## 2.5 部分控制和状态寄存器介绍 ### 2.5.1 W25Q64的指令表 | 指令名称 | 字节 1 (代码) | 字节 2 | 字节 3 | 字节 4 | 字节 5 | 字节 6 | | -------------- | --------------------------- | ------------- | -------- | ------ | ------------ | ------ | | 写使能 | 06h | write_enabled | | | | | | 禁止写 | 04h | | | | | | | 读状态寄存器 1 | 05h | (S7-S0)(2) | | | | | | 读状态寄存器 2 | 35h | (S15-S8)(2) | | | | | | 写状态寄存器 | 01h | (S7-S0) | (S15-S8) | | | | | 页编程 | 02h | A23-A16 | A15-A8 | A7-A0 | (D7-D0) | | | 四倍页编程 | 32h | A23-A16 | A15-A8 | A7-A0 | (D7-D0,…)(3) | | | 块擦除(64KB) | D8h | A23-A16 | A15-A8 | A7-A0 | | | | 块擦除(32KB) | 52h | A23-A16 | A15-A8 | A7-A0 | | | | 扇区擦除(4KB) | 20h | A23-A16 | A15-A8 | A7-A0 | | | | 全片擦除 | C7h/60h | | | | | | | 暂停擦除 | 75h | | | | | | | 恢复擦除 | 7Ah | | | | | | | 掉电模式 | B9h | | | | | | | 高性能模式 | A3h | | | | | | ### 2.5.2 读状态寄存器1 状态寄存器1的内部结构如下: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659491331053766971.png) 状态寄存器1的S0位是当前W25Q64的忙状态;为1的时候表示设备正在执行程序(可能是在擦除芯片)或写状态寄存器指令,这个时候设备将忽略传来的指令, 除了读状态寄存器和擦除暂停指令外,其他写指令或写状态指令都无效, 当 S0 为 0 状态时指示设备已经执行完毕,可以进行下一步操作。 **读状态寄存器1的时序如下:** ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659491348140406138.png) 读取状态寄存器的指令是 8 位的指令。发送指令之前,先将/CS 拉低,再发送指令码“05 h” 或者“35h”。设备收到读取状态寄存器的指令后,将状态信息(高位)依次移位发送出去,读出的状态信息,最低位为 1 代表忙,最低位为 0 代表可以操作,状态信息读取完毕,将片选线拉高。 读状态寄存器指令可以使用在任何时候,即使程序在擦除的过程中或者写状态寄存器周期正在进行中。这可以检测忙碌状态来确定周期是否完成,以确定设备是否可以接受另一个指令。 ### 2.5.3 读制造商ID和芯片ID 时序图如下: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659491360975670153.png) 读取制造商/设备 ID 指令可以读取制造商 ID 和特定的设备 ID。读取之前,拉低 CS 片选信号,接着发送指令代码“90h” ,紧随其后的是一个 24 位地址(A23-A0)000000h。 设备收到指令之后,会发出华邦电子制造商 ID(EFh) 和设备ID(w25q64 为 16h)。如果 24 位地址设置为 000001h ,设备 ID 会先发出,然后跟着制造商 ID。制造商和设备ID可以连续读取。完成指令后,片选信号/ CS 拉高。 ### 2.5.4 全片擦除(C7h/60h) ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659491377737108557.png) 全芯片擦除指令,可以将整个芯片的所有内存数据擦除,恢复到 0XFF 状态。写入全芯片擦除指令之前必须执行设备写使能(发送设备写使能指令 0x06),并判断状态寄存器(状态寄存器位最低位必须等于 0 才能操作)。发送全芯片擦除指令前,先拉低/ CS,接着发送擦除指令码”C7h”或者是”60h”, 指令码发送完毕后,拉高片选线 CS/,,并判断状态位,等待擦除结束。全片擦除指令尽量少用,擦除会缩短设备的寿命。 ### 2.5.5 读数据(03h) ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659491390334216182.png) 读取数据指令允许按顺序读取一个字节的内存数据。当片选 CS/拉低之后,紧随其后是一个 24 位的地址(A23-A0)(需要发送 3 次,每次 8 个字节,先发高位)。芯片收到地址后,将要读的数据按字节大小转移出去,数据是先转移高位,对于单片机,时钟下降沿发送数据,上升沿接收数据。读数据时,地址会自动增加,允许连续的读取数据。这意味着读取整个内存的数据,只要用一个指令就可以读完。数据读取完成之后,片选信号/ CS 拉高。 读取数据的指令序列,如上图所示。如果一个读数据指令而发出的时候,设备正在擦除扇区,或者(忙= 1),该读指令将被忽略,也不会对当前周期有什么影响。 # 三、SPI时序介绍 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间。 SPI是一种高速、高效率的串行接口技术,一共有4根线。通常由一个主模块和一个或多个从模块组成,主模块选择一个从模块进行同步通信,从而完成数据的交换。SPI是一个环形结构,通信时需要至少4根线(在单向传输时3根线也可以)。分别是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选)。 (1)MISO– Master Input Slave Output,主设备数据输入,从设备数据输出; (2)MOSI– Master Output Slave Input,主设备数据输出,从设备数据输入; (3)SCLK – Serial Clock,时钟信号,由主设备产生; (4)CS – Chip Select,从设备使能信号,由主设备控制。 **SPI总线通过时钟极性和相位可以配置成4种时序:** ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659491411123659851.png) **STM32F103参考手册,SPI章节介绍的时序图:** ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20228/3/1659491431673445234.png) **SPI时序比较简单,CPU如果没有硬件支持,可以直接写代码采用IO口模拟,下面是模拟时序的示例的代码:** ```cpp SPI的模式1: u8 SPI_ReadWriteOneByte(u8 tx_data) { u8 i,rx_data=0; SCK=0; //空闲电平(默认初始化情况) for(i=0;i8;i++) { /*1. 主机发送一位数据*/ SCK=0;//告诉从机,主机将要发送数据 if(tx_data&0x80)MOSI=1; //发送数据 else MOSI=0; SCK=1; //告诉从机,主机数据发送完毕 tx_data=1; //继续发送下一位 /*2. 主机接收一位数据*/ rx_data=1; //默认认为接收到0 if(MISO)rx_data|=0x01; } SCK=0; //恢复空闲电平 return rx_data; } SPI的模式2: u8 SPI_ReadWriteOneByte(u8 tx_data) { u8 i,rx_data=0; SCK=0; //空闲电平(默认初始化情况) for(i=0;i8;i++) { /*1. 主机发送一位数据*/ SCK=1;//告诉从机,主机将要发送数据 if(tx_data&0x80)MOSI=1; //发送数据 else MOSI=0; SCK=0; //告诉从机,主机数据发送完毕 tx_data=1; //继续发送下一位 /*2. 主机接收一位数据*/ rx_data=1; //默认认为接收到0 if(MISO)rx_data|=0x01; } SCK=0; //恢复空闲电平 return rx_data; } SPI的模式3: u8 SPI_ReadWriteOneByte(u8 tx_data) { u8 i,rx_data=0; SCK=1; //空闲电平(默认初始化情况) for(i=0;i8;i++) { /*1. 主机发送一位数据*/ SCK=1;//告诉从机,主机将要发送数据 if(tx_data&0x80)MOSI=1; //发送数据 else MOSI=0; SCK=0; //告诉从机,主机数据发送完毕 tx_data=1; //继续发送下一位 /*2. 主机接收一位数据*/ rx_data=1; //默认认为接收到0 if(MISO)rx_data|=0x01; } SCK=1; //恢复空闲电平 return rx_data; } SPI的模式4: u8 SPI_ReadWriteOneByte(u8 tx_data) { u8 i,rx_data=0; SCK=1; //空闲电平(默认初始化情况) for(i=0;i8;i++) { /*1. 主机发送一位数据*/ SCK=0;//告诉从机,主机将要发送数据 if(tx_data&0x80)MOSI=1; //发送数据 else MOSI=0; SCK=1; //告诉从机,主机数据发送完毕 tx_data=1; //继续发送下一位 /*2. 主机接收一位数据*/ rx_data=1; //默认认为接收到0 if(MISO)rx_data|=0x01; } SCK=1; //恢复空闲电平 return rx_data; } ``` # 四、W25Q64的示例代码 ## 4.1 STM32采用硬件SPI读写W25Q64示例代码 ```cpp /* 函数功能:SPI初始化(模拟SPI) 硬件连接: MISO--->PB14 MOSI--->PB15 SCLK--->PB13 */ void SPI_Init(void) { /*开启时钟*/ RCC->APB1ENR|=114; //开启SPI2时钟 RCC->APB2ENR|=13; //PB GPIOB->CRH&=0X000FFFFF; //清除寄存器 GPIOB->CRH|=0XB8B00000; GPIOB->ODR|=0X713; //PB13/14/15上拉--输出高电平 /*SPI2基本配置*/ SPI2->CR1=0X0; //清空寄存器 SPI2->CR1|=015; //选择“双线双向”模式 SPI2->CR1|=011; //使用8位数据帧格式进行发送/接收; SPI2->CR1|=010; //全双工(发送和接收); SPI2->CR1|=19; //启用软件从设备管理 SPI2->CR1|=18; //NSS SPI2->CR1|=07; //帧格式,先发送高位 SPI2->CR1|=0x03;//当总线频率为36MHZ时,SPI速度为18MHZ,高速。 SPI2->CR1|=12; //配置为主设备 SPI2->CR1|=11; //空闲状态时, SCK保持高电平。 SPI2->CR1|=10; //数据采样从第二个时钟边沿开始。 SPI2->CR1|=16; //开启SPI设备。 } /* 函数功能:SPI读写一个字节 */ u8 SPI_ReadWriteOneByte(u8 data_tx) { u16 cnt=0; while((SPI2->SR&11)==0) //等待发送区空--等待发送缓冲为空 { cnt++; if(cnt>=65530)return 0; //超时退出 u16=2个字节 } SPI2->DR=data_tx; //发送一个byte cnt=0; while((SPI2->SR&10)==0) //等待接收完一个byte { cnt++; if(cnt>=65530)return 0; //超时退出 } return SPI2->DR; //返回收到的数据 } /* 函数功能:W25Q64初始化 硬件连接: MOSI--->PB15 MISO--->PB14 SCLK--->PB13 CS----->PB12 */ void W25Q64_Init(void) { /*1. 开时钟*/ RCC->APB2ENR|=13; //PB /*2. 配置GPIO口模式*/ GPIOB->CRH&=0xFFF0FFFF; GPIOB->CRH|=0x00030000; W25Q64_CS=1; //未选中芯片 SPI_Init(); //SPI初始化 } /* 函数功能:读取芯片的ID号 */ u16 W25Q64_ReadID(void) { u16 id; /*1. 拉低片选*/ W25Q64_CS=0; /*2. 发送读取ID的指令*/ SPI_ReadWriteOneByte(0x90); /*3. 发送24位的地址-0*/ SPI_ReadWriteOneByte(0); SPI_ReadWriteOneByte(0); SPI_ReadWriteOneByte(0); /*4. 读取芯片的ID*/ id=SPI_ReadWriteOneByte(0xFF)8; id|=SPI_ReadWriteOneByte(0xFF); /*5. 拉高片选*/ W25Q64_CS=1; return id; } /* 函数功能:检测W25Q64状态 */ void W25Q64_CheckStat(void) { u8 stat=1; while(stat&10) { W25Q64_CS=0; //选中芯片 SPI_ReadWriteOneByte(0x05); //发送读状态寄存器1指令 stat=SPI_ReadWriteOneByte(0xFF); //读取状态 W25Q64_CS=1; //取消选中芯片 } } /* 函数功能:页编程 说 明:一页最多写256个字节。 写数据之前,必须保证空间是0xFF 函数参数: u32 addr:页编程起始地址 u8 *buff:写入的数据缓冲区 u16 len :写入的字节长度 */ void W25Q64_PageWrite(u32 addr,u8 *buff,u16 len) { u16 i; W25Q64_Enabled(); //写使能 W25Q64_CS=0; //选中芯片 SPI_ReadWriteOneByte(0x02); //页编程指令 SPI_ReadWriteOneByte(addr>>16); //24~16地址 SPI_ReadWriteOneByte(addr>>8); //16~8地址 SPI_ReadWriteOneByte(addr); //8~0地址 for(i=0;i/8~0地址 } W25Q64_CS=1; //取消选中芯片 W25Q64_CheckStat(); //检测芯片忙状态 } /* 函数功能:连续读数据 函数参数: u32 addr:读取数据的起始地址 u8 *buff:读取数据存放的缓冲区 u32 len :读取字节的长度 */ void W25Q64_ReadByteData(u32 addr,u8 *buff,u32 len) { u32 i; W25Q64_CS=0; //选中芯片 SPI_ReadWriteOneByte(0x03); //读数据指令 SPI_ReadWriteOneByte(addr>>16); //24~16地址 SPI_ReadWriteOneByte(addr>>8); //16~8地址 SPI_ReadWriteOneByte(addr); //8~0地址 for(i=0;i W25Q64_CS=1; //取消选中芯片 } /* 函数功能:擦除一个扇区 函数参数: u32 addr:擦除扇区的地址范围 */ void W25Q64_ClearSector(u32 addr) { W25Q64_Enabled(); //写使能 W25Q64_CS=0; //选中芯片 SPI_ReadWriteOneByte(0x20); //扇区擦除指令 SPI_ReadWriteOneByte(addr>>16); //24~16地址 SPI_ReadWriteOneByte(addr>>8); //16~8地址 SPI_ReadWriteOneByte(addr); //8~0地址 W25Q64_CS=1; //取消选中芯片 W25Q64_CheckStat(); //检测芯片忙状态 } /* 函数功能:写使能 */ void W25Q64_Enabled(void) { W25Q64_CS=0; //选中芯片 SPI_ReadWriteOneByte(0x06); //写使能 W25Q64_CS=1; //取消选中芯片 } /* 函数功能:指定位置写入指定个数的数据,不考虑擦除问题 注意事项:W25Q64只能将1写为,不能将0写为1。 函数参数: u32 addr---写入数据的起始地址 u8 *buff---写入的数据 u32 len---长度 */ void W25Q64_WriteByteDataNoCheck(u32 addr,u8 *buff,u32 len) { u32 page_remain=256-addr%256; //计算当前页还可以写下多少数据 if(len=page_remain) //如果当前写入的字节长度小于剩余的长度 { page_remain=len; } while(1) { W25Q64_PageWrite(addr,buff,page_remain); if(page_remain==len)break; //表明数据已经写入完毕 buff+=page_remain; //buff向后偏移地址 addr+=page_remain; //起始地址向后偏移 len-=page_remain; //减去已经写入的字节数 if(len>256)page_remain=256; //如果大于一页,每次就直接写256字节 else page_remain=len; } } /* 函数功能:指定位置写入指定个数的数据,考虑擦除问题,完善代码 函数参数: u32 addr---写入数据的起始地址 u8 *buff---写入的数据 u32 len---长度 说明:擦除的最小单位扇区,4096字节 */ static u8 W25Q64_READ_WRITE_CHECK_BUFF[4096]; void W25Q64_WriteByteData(u32 addr,u8 *buff,u32 len) { u32 i; u32 len_w; u32 sector_addr; //存放扇区的地址 u32 sector_move; //扇区向后偏移的地址 u32 sector_size; //扇区大小。(剩余的空间大小) u8 *p=W25Q64_READ_WRITE_CHECK_BUFF;//存放指针 sector_addr=addr/4096; //传入的地址是处于第几个扇区 sector_move=addr%4096; //计算传入的地址存于当前的扇区的偏移量位置 sector_size=4096-sector_move; //得到当前扇区剩余的空间 if(len=sector_size) { sector_size=len; //判断第一种可能性、一次可以写完 } while(1) { W25Q64_ReadByteData(addr,p,sector_size); //读取剩余扇区里的数据 for(i=0;i } if(i!=sector_size) //判断是否需要擦除 { W25Q64_ClearSector(sector_addr*4096); } // for(i=0;i/ { // W25Q64_READ_WRITE_CHECK_BUFF[i]=buff[len_w++]; //// } // W25Q64_WriteByteDataNoCheck(addr,W25Q64_READ_WRITE_CHECK_BUFF,sector_size); W25Q64_WriteByteDataNoCheck(addr,buff,sector_size); if(sector_size==len)break; addr+=sector_size; //向后偏移地址 buff+=sector_size ;//向后偏移 len-=sector_size; //减去已经写入的数据 sector_addr++; //校验第下个扇区 if(len>4096) //表明还可以写一个扇区 { sector_size=4096;//继续写一个扇区 } else { sector_size=len; //剩余的空间可以写完 } } } ``` ## 4.2 STM32采用硬件SPI读写W25Q64示例代码 ```cpp #include "spi.h" /* 函数功能:SPI初始化(模拟SPI) 硬件连接: MISO--->PB14 MOSI--->PB15 SCLK--->PB13 */ void SPI_Init(void) { /*1. 开时钟*/ RCC->APB2ENR|=13; //PB /*2. 配置GPIO口模式*/ GPIOB->CRH&=0x000FFFFF; GPIOB->CRH|=0x38300000; /*3. 上拉*/ SPI_MOSI=1; SPI_MISO=1; SPI_SCLK=1; } /* 函数功能:SPI读写一个字节 */ u8 SPI_ReadWriteOneByte(u8 data_tx) { u8 data_rx=0; //存放读取的数据 u8 i; for(i=0;i8;i++) { SPI_SCLK=0; //准备发送数据 if(data_tx&0x80)SPI_MOSI=1; else SPI_MOSI=0; data_tx=1; //依次发送最高位 SPI_SCLK=1; //表示主机数据发送完成,表示从机发送完毕 data_rx=1; //表示默认接收的是0 if(SPI_MISO)data_rx|=0x01; } return data_rx; } #include "W25Q64.h" /* 函数功能:W25Q64初始化 硬件连接: MOSI--->PB15 MISO--->PB14 SCLK--->PB13 CS----->PB12 */ void W25Q64_Init(void) { /*1. 开时钟*/ RCC->APB2ENR|=13; //PB /*2. 配置GPIO口模式*/ GPIOB->CRH&=0xFFF0FFFF; GPIOB->CRH|=0x00030000; W25Q64_CS=1; //未选中芯片 SPI_Init(); //SPI初始化 } /* 函数功能:读取芯片的ID号 */ u16 W25Q64_ReadID(void) { u16 id; /*1. 拉低片选*/ W25Q64_CS=0; /*2. 发送读取ID的指令*/ SPI_ReadWriteOneByte(0x90); /*3. 发送24位的地址-0*/ SPI_ReadWriteOneByte(0); SPI_ReadWriteOneByte(0); SPI_ReadWriteOneByte(0); /*4. 读取芯片的ID*/ id=SPI_ReadWriteOneByte(0xFF)8; id|=SPI_ReadWriteOneByte(0xFF); /*5. 拉高片选*/ W25Q64_CS=1; return id; } /* 函数功能:检测W25Q64状态 */ void W25Q64_CheckStat(void) { u8 stat=1; while(stat&10) { W25Q64_CS=0; //选中芯片 SPI_ReadWriteOneByte(0x05); //发送读状态寄存器1指令 stat=SPI_ReadWriteOneByte(0xFF); //读取状态 W25Q64_CS=1; //取消选中芯片 } } /* 函数功能:页编程 说 明:一页最多写256个字节。 写数据之前,必须保证空间是0xFF 函数参数: u32 addr:页编程起始地址 u8 *buff:写入的数据缓冲区 u16 len :写入的字节长度 */ void W25Q64_PageWrite(u32 addr,u8 *buff,u16 len) { u16 i; W25Q64_Enabled(); //写使能 W25Q64_CS=0; //选中芯片 SPI_ReadWriteOneByte(0x02); //页编程指令 SPI_ReadWriteOneByte(addr>>16); //24~16地址 SPI_ReadWriteOneByte(addr>>8); //16~8地址 SPI_ReadWriteOneByte(addr); //8~0地址 for(i=0;i/8~0地址 } W25Q64_CS=1; //取消选中芯片 W25Q64_CheckStat(); //检测芯片忙状态 } /* 函数功能:连续读数据 函数参数: u32 addr:读取数据的起始地址 u8 *buff:读取数据存放的缓冲区 u32 len :读取字节的长度 */ void W25Q64_ReadByteData(u32 addr,u8 *buff,u32 len) { u32 i; W25Q64_CS=0; //选中芯片 SPI_ReadWriteOneByte(0x03); //读数据指令 SPI_ReadWriteOneByte(addr>>16); //24~16地址 SPI_ReadWriteOneByte(addr>>8); //16~8地址 SPI_ReadWriteOneByte(addr); //8~0地址 for(i=0;i W25Q64_CS=1; //取消选中芯片 } /* 函数功能:擦除一个扇区 函数参数: u32 addr:擦除扇区的地址范围 */ void W25Q64_ClearSector(u32 addr) { W25Q64_Enabled(); //写使能 W25Q64_CS=0; //选中芯片 SPI_ReadWriteOneByte(0x20); //扇区擦除指令 SPI_ReadWriteOneByte(addr>>16); //24~16地址 SPI_ReadWriteOneByte(addr>>8); //16~8地址 SPI_ReadWriteOneByte(addr); //8~0地址 W25Q64_CS=1; //取消选中芯片 W25Q64_CheckStat(); //检测芯片忙状态 } /* 函数功能:写使能 */ void W25Q64_Enabled(void) { W25Q64_CS=0; //选中芯片 SPI_ReadWriteOneByte(0x06); //写使能 W25Q64_CS=1; //取消选中芯片 } /* 函数功能:指定位置写入指定个数的数据,不考虑擦除问题 注意事项:W25Q64只能将1写为,不能将0写为1。 函数参数: u32 addr---写入数据的起始地址 u8 *buff---写入的数据 u32 len---长度 */ void W25Q64_WriteByteDataNoCheck(u32 addr,u8 *buff,u32 len) { u32 page_remain=256-addr%256; //计算当前页还可以写下多少数据 if(len=page_remain) //如果当前写入的字节长度小于剩余的长度 { page_remain=len; } while(1) { W25Q64_PageWrite(addr,buff,page_remain); if(page_remain==len)break; //表明数据已经写入完毕 buff+=page_remain; //buff向后偏移地址 addr+=page_remain; //起始地址向后偏移 len-=page_remain; //减去已经写入的字节数 if(len>256)page_remain=256; //如果大于一页,每次就直接写256字节 else page_remain=len; } } /* 函数功能:指定位置写入指定个数的数据,考虑擦除问题,完善代码 函数参数: u32 addr---写入数据的起始地址 u8 *buff---写入的数据 u32 len---长度 说明:擦除的最小单位扇区,4096字节 */ static u8 W25Q64_READ_WRITE_CHECK_BUFF[4096]; void W25Q64_WriteByteData(u32 addr,u8 *buff,u32 len) { u32 i; u32 sector_addr; //存放扇区的地址 u32 sector_move; //扇区向后偏移的地址 u32 sector_size; //扇区大小。(剩余的空间大小) u8 *p=W25Q64_READ_WRITE_CHECK_BUFF;//存放指针 sector_addr=addr/4096; //传入的地址是处于第几个扇区 sector_move=addr%4096; //计算传入的地址存于当前的扇区的偏移量位置 sector_size=4096-sector_move; //得到当前扇区剩余的空间 if(len=sector_size) { sector_size=len; //判断第一种可能性、一次可以写完 } while(1) { W25Q64_ReadByteData(addr,p,sector_size); //读取剩余扇区里的数据 for(i=0;i } if(i!=sector_size) //判断是否需要擦除 { W25Q64_ClearSector(sector_addr*4096); } W25Q64_WriteByteDataNoCheck(addr,buff,sector_size); if(sector_size==len)break; addr+=sector_size; //向后偏移地址 buff+=sector_size ;//向后偏移 len-=sector_size; //减去已经写入的数据 sector_addr++; //校验第下个扇区 if(len>4096) //表明还可以写一个扇区 { sector_size=4096;//继续写一个扇区 } else { sector_size=len; //剩余的空间可以写完 } } } ```
  • [技术干货] STM32F103实现IAP在线升级应用程序
    # 一、环境介绍 **MCU:** STM32F103ZET6 **编程IDE:** Keil5.25 # 二、 IAP介绍 > IAP,全称是“In-Application Programming”,中文解释为“在程序中编程”。IAP是一种对通过微控制器的对外接口(如USART,IIC,CAN,USB,以太网接口甚至是无线射频通道)对正在运行程序的微控制器进行内部程序的更新的技术(注意这完全有别于ICP或者ISP技术)。 > > ICP(In-Circuit Programming)技术即通过在线仿真器对单片机进行程序烧写,而ISP技术则是通过单片机内置的bootloader程序引导的烧写技术。无论是ICP技术还是ISP技术,都需要有机械性的操作如连接下载线,设置跳线帽等。若产品的电路板已经层层密封在外壳中,要对其进行程序更新无疑困难重重,若产品安装于狭窄空间等难以触及的地方,更是一场灾难。但若进引入了IAP技术,则完全可以避免上述尴尬情况,而且若使用远距离或无线的数据传输方案,甚至可以实现远程编程和无线编程。这绝对是ICP或ISP技术无法做到的。某种微控制器支持IAP技术的首要前提是其必须是基于可重复编程闪存的微控制器。STM32微控制器带有可编程的内置闪存,同时STM32拥有在数量上和种类上都非常丰富的外设通信接口,因此在STM32上实现IAP技术是完全可行的。 > > 实现IAP技术的核心是一段预先烧写在单片机内部的IAP程序。这段程序主要负责与外部的上位机软件进行握手同步,然后将通过外设通信接口将来自于上位机软件的程序数据接收后写入单片机内部指定的闪存区域,然后再跳转执行新写入的程序,最终就达到了程序更新的目的。 > > 在STM32微控制器上实现IAP程序之前首先要回顾一下STM32的内部闪存组织架构和其启动过程。STM32的内部闪存地址起始于0x8000000,一般情况下,程序文件就从此地址开始写入。此外STM32是基于Cortex-M3内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动。而这张“中断向量表”的起始地址是0x8000004,当中断来临,STM32的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。最后还需要知道关键的一点,通过修改STM32工程的链接脚本可以修改程序文件写入闪存的起始地址。 > > 在STM32微控制器上实现IAP方案,除了常规的串口接收数据以及闪存数据写入等常规操作外,还需注意STM32的启动过程和中断响应方式。 **下图显示了STM32常规的运行流程:** ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114285706288032.png) ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114295727781372.png) > **图解读如下:** > 1、 STM32复位后,会从地址为0x8000004处取出复位中断向量的地址,并跳转执行复位中断服务程序。 > 2、 复位中断服务程序执行的最终结果是跳转至C程序的main函数,而main函数应该是一个死循环,是一个永不返回的函数。 > 3、 在main函数执行的过程中,发生了一个中断请求,此时STM32的硬件机制会将PC指针强制指回中断向量表处。 > 4、 根据中断源进入相应的中断服务程序。 > 5、 中断服务程序执行完毕后,程序再度返回至main函数中执行。 **若在STM32中加入了IAP程序:** ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114347153461341.png) ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114359152285209.png) > 1、 STM32复位后,从地址为0x8000004处取出复位中断向量的地址,并跳转执行复位中断服务程序,随后跳转至IAP程序的main函数。 > > 2、 执行完IAP过程后(STM32内部多出了新写入的程序,地址始于0x8000004+N+M)跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数。新程序的main函数应该也具有永不返回的特性。同时应该注意在STM32的内部存储空间在不同的位置上出现了2个中断向量表。 > > 3、 在新程序main函数执行的过程中,一个中断请求来临,PC指针仍会回转至地址为0x8000004中断向量表处,而并不是新程序的中断向量表,注意到这是由STM32的硬件机制决定的。 > > 4、 根据中断源跳转至对应的中断服务,注意此时是跳转至了新程序的中断服务程序中。 > > 5、 中断服务执行完毕后,返回main函数。 # 二、hex文件与bin文件区别 > Intel HEX文件是记录文本行的ASCII文本文件,在Intel HEX文件中,每一行是一个HEX记录,由十六进制数组成的机器码或者数据常量。Intel HEX文件经常被用于将程序或数据传输存储到ROM、EPROM,大多数编程器和模拟器使用Intel HEX文件。 > 很多编译器的支持生成HEX格式的烧录文件,尤其是Keil c。但是编程器能够下载的往往是BIN格式,因此HEX转BIN是每个编程器都必须支持的功能。HEX格式文件以行为单位,每行由“:”(0x3a)开始,以回车键结束(0x0d,0x0a)。行内的数据都是由两个字符表示一个16进制字节,比如”01”就表示数0x01;”0a”,就表示0x0a。对于16位的地址,则高位在前低位在后,比如地址0x010a,在HEX格式文件中就表示为字符串”010a”。 > > **hex和bin文件格式** > Hex文件,这里指的是Intel标准的十六进制文件,也就是机器代码的十六进制形式,并且是用一定文件格式的ASCII码来表示。具体格式介绍如下: Intel hex 文件常用来保存单片机或其他处理器的目标程序代码。它保存物理程序存储区中的目标代码映象。一般的编程器都支持这种格式。  hex和bin文件格式Hex文件,这里指的是Intel标准的十六进制文件,也就是机器代码的十六进制形式,并且是用一定文件格式的ASCII码来表示。具体格式介绍如下: Intel hex 文件常用来保存单片机或其他处理器的目标程序代码。它保存物理程序存储区中的目标代码映象。一般的编程器都支持这种格式。 # 三、使用Keil软件完成hex文件转bin文件 ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114374589856141.png) ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114383525504852.png) **选项框里的代码:** C:\app_setup\for_KEIL\ARM\ARMCC\bin\fromelf.exe --bin -o ./OBJECT/STM32_MD.bin ./OBJECT/STM32_MD.axf **解析如下:** C:\app_setup\for_KEIL\ARM\ARMCC\bin\fromelf.exe:是keil软件安装目录下的一个工具,用于生成bin --bin -o ./OBJECT/STM32_MD.bin :指定生成bin文件的目录和名称 ./OBJECT/STM32_MD.axf :指定输入的文件. 生成hex文件需要axf文件 **新工程的编译指令:** C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o ./obj/STM32HD.bin ./obj/STM32HD.axf ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114413620573770.png) ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114425391426279.png) 将该文件下载到STM32内置FLASH,复位开发板,即可启动程序。 # 四、 使用win hex软件将bin文件搞成数组 ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114441287402730.png) ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114453194158465.png) 生成数组之后,可以直接将数组编译到程序里,然后使用**STM32****内置FLASH****编程代码**,将该程序烧写到内置FLASH里,再复位开发板即可运行新的程序。 # 五、 Keil编译程序大小计算 **Program Size: Code=x RO-data=x RW-data=x ZI-data=x** **的含义** 1. Code(代码): 程序所占用的FLASH大小,存储在FLASH. \2. RO-data(只读的数据): Read-only-data,程序定义的常量,如const型,存储在FLASH中。 \3. RW-data(有初始值要求的、可读可写的数据): \4. Read-write-data,已经被初始化的变量,存储在FLASH中。初始化时RW-data从flash拷贝到SRAM。 \5. ZI-data:Zero-Init-data,未被初始化的可读写变量,存储在SRAM中。ZI-data不会被算做代码里因为不会被初始化。 ROM(Flash) size = Code + RO-data + RW-data; RAM size = RW-data + ZI-data 简单的说就是在烧写的时候是FLASH中的被占用的空间为:Code+RO Data+RW Data 程序运行的时候,芯片内部RAM使用的空间为: RW Data + ZI Data # 六、工程编译信息与堆栈信息查看 ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114483318950484.png) 对于没有OS的程序,堆栈大小是在 startup.s 里设置的: Stack_Size EQU 0x00000800 对于使用用 uCos 的系统,OS自带任务的堆栈,在 os_cfg.h 里定义: ```cpp /* ——————— TASK STACK SIZE ———————- */ #define OS_TASK_TMR_STK_SIZE 128 /* Timer task stack size (# of OS_STK wide entries) */ #define OS_TASK_STAT_STK_SIZE 128 /* Statistics task stack size (# of OS_STK wide entries) */ #define OS_TASK_IDLE_STK_SIZE 128 /* Idle task stack size (# of OS_STK wide entries) */ ``` **用户程序的任务堆栈,在 app_cfg.h 里定义:** ```cpp #define APP_TASK_MANAGER_STK_SIZE 512 #define APP_TASK_GSM_STK_SIZE 512 #define APP_TASK_OBD_STK_SIZE 512 #define OS_PROBE_TASK_STK_SIZE 128 ``` **总结:** 1, 合理设置堆栈很重要 2, 多种方法结合,相互核对、校验 3, 尽量避免大数组,如果一定要用,尽量定义为 全局变量,使其不占用堆栈空间, 如果函数有重入可能性,则要注意保护。 # 七、实现STM32在线升级程序 ## 7.1 升级的思路与步骤 \1. 首先得完成STM32内置FLASH编程操作 \2. 将(升级的程序)新的程序编译生成bin文件(编译之前需要在Keil软件里设置FLASH的起始位置) \3. 创建一个专门用于升级的boot程序(**IAP Bootloader)** \4. 使用网络、串口、SD卡等方式接收到bin文件,再将bin文件烧写到STM32内置FLASH里 \5. 设置主堆栈指针 \6. 将用户代码区第二个字(**第4****个字节**)为程序开始地址(强制转为函数指针) \7. **执行函数,进行程序跳转** ## 7.2 待升级的程序FLASH起始设置 **Bootloader** 的程序大小先固定为: 20KB,最好是越小越好,可以预留更加多的空间给APP程序使用。 20KB----->20480Byte-----> 0x5000 STM32内置FLASH闪存的起始地址是: 0x08000000 ,大小是512KB。 现在将内置FLASH闪存前20KB的空间留给**Bootloader**程序使用,后面剩下的空间就给APP程序使用。 APP程序的起始位置就可以设置为: 0x08000000+ 0x5000=0x08005000 剩余的大小就是: 512KB-20KB=492KB------>503808Byte-------->0x7B000 **设置FLASH的起始位置(APP主程序):** ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114501954139742.png) **中断向量表偏移量设置** ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114523843125518.png) **设置编译bin文件** ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114535511383593.png) ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114548222203324.png) ## 7.3 Bootloader的程序设置 ```cpp //设置写入的地址,必须偶数,因为数据读写都是按照2个字节进行 #define FLASH_APP_ADDR 0x08005000 //应用程序存放到FLASH中的起始地址 int main() { printf("UART1 OK.....\n"); printf("进入IAP Bootloader程序!\n"); while(1) { key=KEY_Scanf(); if(key==1) //KEY1按下,写入STM32 FLASH { printf("正在更新IAP程序...............\n"); iap_write_appbin(FLASH_APP_ADDR,(u8*)app_bin_data,sizeof(app_bin_data));//烧写新的程序到内置FLASH printf("程序更新成功....\n"); iap_load_app(FLASH_APP_ADDR);//执行FLASH APP代码 } } } /* 函数功能:跳转到应用程序段 appxaddr:用户代码起始地址. */ typedef void (*iap_function)(void); //定义一个函数类型的参数. void IAP_LoadApp(u32 app_addr) { //给函数指针赋值合法地址 jump2app=(iap_function)*(vu32*)(app_addr+4);//用户代码区第二个字为程序开始地址(复位地址) __set_MSP(*(vu32*)app_addr); //设置主堆栈指针 jump2app(); //跳转到APP. } ``` ![image.png](https://bbs-img.huaweicloud.com/blogs/img/20220706/1657114560110736229.png)
  • [行业资讯] 国产存储的破晓之光
    编者按:自1956年中国将作为国家重要的发展领域后,今年是第66个年头。回望66年的发展,从无到有、从小到大,半导体产业经历了风雨坎坷同时又迸发出无限的生机。在中国“十四五”提出数字经济发展规划,瞄准等战略性领域之际,半导体产业纵横推出“国产化进程”系列专题,讲述当今中国半导体各领域发展进程,解析国产化最新态势,本期为“国产化进程”专题集成篇第五篇文章:存储。半导体产业纵横编辑部WSTS数据显示,2021年全球存储市场规模为1582亿美元,同比上升34.6%。2022年全球半导体市场规模将增长8.8%,达到6014.9亿美元,其中,存储类别的增长为8.55%,规模达到1716.82亿美元,占整个半导体市场的28.5%。在众多的存储类别中,市场规模最大的是DRAM和NAND Flash。2021年全球 DRAM 市场规模约占整个存储市场的56%(约869亿美元), NAND Flash 市场规模约占整个存储市场的41%(约636亿美元), NOR Flash 市场规模约占整个存储市场的2%(约31亿美元),其他存储(EEPROM、EPROMROM 、SRAM等)合计占比为1%(约16亿美元)。2022 年,DRAM、NAND 闪存市场将分别增长 25%、24% 到 1180 亿美元、830 亿美元,均创历史新高。国储市场的大蛋糕从市场需求来看,中国已成为全球存储最重要的需求市场之一。CFM闪存市场数据显示,在中国的DRAM和NAND Flash销售规模占据全球30%以上市场份额。另外,随着国民经济结构转型与消费升级,对电子产品和服务的需求也与日俱增,存储作为电子产品的关键部件,受益于整个社会信息化进程。未来,随着、云计算、物联网、大数据等产业的快速发展带来存储的需求持续提升,将为存储提供更加广阔的发展空间。存储需求扩张的周期,为国内企业带来了机遇。毕竟,在一个增量市场,才有更多试错的机会,而在一个饱和的市场里,机会则微乎其微。这对于当下的中国企业至关重要。随着扩产、增资、IPO的消息传出,国产存储正在发出属于自己的光芒。国内存储龙头长江存储正筹备产能的扩张。据此前的消息指出,长江存储早在2020年中就已筹建存储基地项目二期,目前正在加速推进,预计未来两年就能投产,其二期项目的月产能为20万片,一期项目为10万片,如此两年多之后NAND flash芯片产能将增加两倍。产能方面,长鑫也不甘落后。公开资料显示,长鑫在2020年、2021年分别实现了4.5万片晶圆/月、6万片晶圆/月的目标,2022年的产能目标是12万片晶圆/月,未来的产能目标是30万片晶圆/月。截止2021年,全球DRAM总产能大约是150万片/月,预测到2022年底,会提升至160万片/月左右,届时长鑫存储的12万片产能将获得全球市场份额的8%左右。以存储为主要业务的芯天下提交了创业板IPO申请。4月28日,深交所正式受理了芯天下IPO上市申请。芯天下表示,公司本次发行募集资金将紧密围绕公司主营业务,主要投向NOR Flash产品研发升级和产业化项目、NANDFlash产品研发升级和产业化项目、存储研发中心建设项目及补充流动资金。其中,NOR Flash产品研发升级和产业化项目拟在公司现有NOR Flash产品线的基础上,进一步研发55nm/50nm/4xnm工艺制程的NOR Flash产品,完成产品工艺技术的迭代升级。NAND Flash产品研发升级和产业化项目拟在公司现有SLC NAND Flash产品线的基础上,研发2xnm工艺制程的单芯片SPI NAND产品及超低功耗SLC NAND Flash产品,形成更丰富的SLC NAND Flash产品系列。5月22日晚间,韦尔股份宣布,拟以不超过40亿元增持北京君正股票。增持后累计持有北京君正股份数量不超过5000万股,不超过北京君正总股本的10.38%。本次交易构成关联交易,不构成重大资产重组。据韦尔股份在公告中介绍,北京君正的在汽车市场有着较好的客户基础,而公司是全球车载CIS的领军企业,未来公司与北京君正存在较大业务战略合作机会。扩产、增资、IPO,国产存储正当时。国产存储与国外差距几何?NANDNAND闪存长期被几家大厂“霸屏”。据CMF数据,2021年Q4,占据NAND闪存市场份额Top6的厂商依次为:(34%)、铠侠(19%)、SK海力士(14%)、西部数据(14%)、美光(10%)、(5%),而留给其他厂商的市场份额仅剩下3%。从NAND闪存的技术路线来看,从2D到3D,各大厂商都朝着堆叠层数更高的方向不断发展。目前,业内比较先进且投入量产的NAND闪存为176层闪存,美光、SK海力士、三星也都实现了176层NAND闪存的量产。另外,美光已经隆重宣布了业内首创的232层3D NAND存储解决方案,高调引领NAND闪存闯入200层大战。国内在NAND闪存方面掌握最先进技术的是长江存储,128层NAND闪存已经量产,而近期据业内人士透露,长江存储已向少数客户交付了其内部开发的192层3D NAND闪存样品。DRAM再来看DRAM。DDRM的全球市场呈现出明显的三足鼎立的局面,在市占率方面,据IC Insights,2021年占据DRAM市场营收榜单Top3的分别为三星(44%)、SK 海力士(27.7%)、美光(22.8%),三大供应商占据 2021 年 DRAM 市场份额的 94%。DRAM制程工艺进入20nm以后,制造难度越来越高。全球前三大厂商—三星、SK海力士、美光于2016-2017年进入1Xnm(16nm-19nm)阶段,2018-2019年进入1Ynm(14nm-16nm)阶段,2020年进于1Znm(12nm-14nm)时代。目前,进入第四阶段1anm(10nm)的研发。目前,国内的长鑫存储还处于1Xnm(16nm-19nm)阶段,与龙头大厂还是有一定的差距。总的来说,国内的存储产业发展起步比较晚,长江存储和长鑫存储2016年成立,分别补上了中国在NAND Flash和DRAM市场核心供应环节的空白。虽然并未达到像龙头一样的市占率,但是已经缩小了与业内先进水平的差距。要注意的是,技术仍是半导体产业的第一生产力。半导体产品更新换代及技术升级速度较快,持续研发新技术、推出新产品是各家厂商在市场中保持优势的重要手段。困住国产存储发展的两大难题当下国产存储发展还应解决两大难题:第一,Fabless模式较多,很难应对供应链产能短缺问题;第二,成本尚不具备优势。从存储生产的形式来看,IDM无疑是一个较好的形式。国外存储的龙头厂商都是IDM模式,都有自己的晶圆厂,可根据自己的生产需求来及时调整自己的产能,有更多的自主性。但是,由于集成电路行业的特殊性,建立晶圆代工厂的成本相当高,所以对于国内非存储龙头大厂来说,Fabless其实是他们权衡利弊之后最好的选择。但是,一旦产能短缺,或者代工厂调整价格,都有可能导致公司供货紧张、产能受限对公司的日常经营和盈利能力造成不利影响。再一个是存储芯片行业具有资本及技术密集型特点。由于产品标准化程度高,行业集中度高,规模效应较为明显,存储行业巨头及行业先进入者,在规模、工艺成熟度等方面领先于后来者,在成本方面具备较为明显的优势。大部分Fabless厂商,因为对代工厂下单的规模较小,所以没有很强的议价能力,导致公司的成本会比较高。国内存储产业还面临着投入不足的困境,半导体产业的技术和产能扩充,都需要数额较大的投资,这对于国内初露矛头的厂商来说还是有一定难度的,他们并不能像龙头一样进行自如的投资。国内存储厂商的破局之刃随着国内半导体产业不断发展,以长江存储和合肥长鑫为代表的存储厂商也逐渐展露头角。长江存储长江存储可谓是3D NAND的国产之光,不断缩短和世界领先水平的距离,将有望引领国产 NAND 产业崛起。长江存储用短短3年时间实现了从32层到64层再到128层的跨越,而近期据业内人士透露,长江存储已向少数客户交付了其内部开发的192层3D NAND闪存样品。2019 年 9 月,公司还创新使用Xtacking?1.0 架构闪存技术,成功量产64 层 TLC 3D NAND 闪存。不同于传统 3D NAND 架构,Xtacking?技术属于自主创新。在传统 NAND 架构中,I/O 及记忆单元操作的外围电路和存储单元在同一片晶圆上制造。在长江存储 Xtacking?架构中,I/O 及记忆单元操作的外围电路被生产在一片晶圆上,而存储单元在另一片晶圆上被独立加工,当两片晶圆各自加工完成后,Xtacking?技术只需一个处理步骤即可通过数十亿根金属垂直互联通道将二者键合接通电路,并封装到同一个芯片中。除此之外,长江存储跳脱的技术路线也在现在成为了一段”佳话“,长江存储决定跳过业界常见的96层,直接研发128层3D NAND。长鑫存储长鑫存储成立于2016年5月,公司自成立以来就致力于DRAM的研发与量产,并已成功推出面向主流市场的DDR4、LPDDR4和LPDDR4X等。良率在70-75%,未来2-3年内将推进低功耗、高速率LPDDR5DRAM产品开发。从技术来看,长鑫存储的核心技术来源于奇梦达遗留的DRAM专利,后来为了规避可能存在的专利风险,长鑫存储投入25 亿美元的研发费用对原有芯片架构进行重新设计。基于这一专利技术,长鑫存储又成功量产出19nm工艺的DDR4和LPDDR4,成为全球第四家DRAM产品采用20nm以下工艺的厂商,是目前中国大陆唯一能够自主生产DRAM的厂商。福建晋华曾经,长江存储、长鑫存储、福建晋华,被称为“中国存储三雄”。2016年,福建晋华与联电签署技术合作协定,福建晋华委托联电开发DRAM相关技术,开发出的技术成果由双方共同所有。福建晋华也正因为这个项目,2017年卷入了和美光的专利之战中,美光指控联电和福建晋华盗用了自己的内存芯片技术。2019年1月底,联电退出福建晋华DRAM项目。一直到2021年11月,联电和美光达成世纪大和解,而目前福建晋华方面审查还没有最终结果。虽被百般打压,但福建晋华仍百折不挠。2021年初,福建工信厅发布一则消息称,泉州晋华成功研制出具备自主产权的25nm内存芯片并小批量试产。武汉新芯武汉新芯于2006年在武汉成立,是一家先进的集成电路研发与制造企业。武汉新芯拥有2座12英寸晶圆厂,每座晶圆厂产能可达3万片+/月。在NOR Flash领域,武汉新芯已经积累了十多年的制造经验,提供从65nm到45nm的高性能NOR Flash技术服务,是中国先进的NORFlash晶圆制造商之一。此外,武汉新芯也推出了自有品牌SPINOR Flash产品。兆易创新从存储芯片三大主流产品布局来看,兆易创新形成了NOR、NAND和DRAM三大存储芯片的全平台布局,其NOR Flash产品涵盖了市场绝大部分的容量类型。其NAND Flash产品属于SLC NAND。2016年,兆易创新推出了首颗DRAM产品GDQ2BFAA系列,标志着正式入局DRAM这一主流存储市场。值得一提的是,在中国市场,兆易创新的NOR FLASH市场占有率排名第一,2021年,出货量为32.88 亿颗。此外,兆易创新还通过增资长鑫存储的母公司睿力集成加码DRAM业务,公司于2020年12月、2021年9月两次向睿力集成增资共计8亿元人民币。紫光国芯作为紫光国微的子公司,紫光国芯主要从事存储设计开发、自有品牌存储芯片产品销售,以及开发、测试服务,是“国家规划布局内集成企业”和“国家火炬计划重点高新技术企业”,建设了完整先进的DRAM存储测试分析工程中心,同时拥有世界主流动态随机存储和闪存存储设计开发技术和经验。芯天下芯天下是业内最早提供SPI NANDFlash的厂商之一,公司现有主要产品包括NORFlash和SLC NAND Flash,广泛应用于、网络通讯、物联网、工业与医疗等领域。北京君正北京君正的存储业务主要有SRAM、DRAM和Flash三大类别。公司DRAM产品开发主要针对具有较高技术壁垒的专业级应用领域,能够满足工业、医疗、主干通讯和车规等级产品的要求,具备在极端环境下稳定工作、节能降耗等特点。公司也进行了包括了从DDR2、LPDDR2、DDR3到DDR4、LPDDR4等不同种类不同容量的产品研发,根据不同产品的进度情况,部分新产品仍在研发阶段,部分新产品完成了投片并正在进行工程样品的生产,部分新产品已完成工程样品的生产并根据测试结果展开了量产方面的工作。北京君正的Flash产品线包括了目前全球主流的NOR Flash和NAND Flash。东芯半导体东芯半导体的主要产品为非易失性存储芯片NAND Flash、NOR Flash以及DRAM。东芯半导体NAND Flash产品核心技术优势明显,尤其是SPI NAND Flash,公司的NANDFlash凭借产品品类丰富、功耗低、可靠性高等特点,被广泛应用于通讯设备、安防监控、可穿戴设备及移动终端等领域。SPI NOR Flash存储容量覆盖2Mb至256Mb,并支持多种数据传输模式,目前公司已经为三星电子、、传音控股、等终端客户提供产品。DDR3系列产品在通讯设备、移动终端等领域应用广泛;LPDDR系列产品适合在智能终端、可穿戴设备等产品中使用。聚辰半导体聚辰半导体绝大部分的收入来自EEPROM 产品,该类产品相较于 NOR Flash 的容量更小、擦写次数高,因此适用于各类电子设备的小容量数据存储和反复擦写的需求,广泛应用于智能手机摄像头、液晶面板、模块、通讯、及周边、医疗仪器、白色家电、、等领域。此外,公司还积极开拓NOR Flash业务。恒烁半导体恒烁半导体的主营产品为 NOR Flash,2016年,公司便已成功量产多款65nm串行NORFlash产品;2018年底,公司完成了不同电压下NOR Flash的全产线布局;2020年,恒烁半导体推出了50nm串行128 Mb NOR Flash。结语国内存储产业发展迅速,各有侧重,也正在一步步缩短与国际厂商的差距,但与此同时,我们也要清晰的认识到,与国外技术相比,我国相关产业的整体水平仍然不够,短时间内想要扭转国际巨头主导的产业格局依然很难。若想要与国际对手势均力敌,仍需时间与技术的沉淀。       原文标题 : 国产存储的破晓之光
  • [技术干货] ColdFusion与FLASH通信轻松入门教程
    使用前准备: 你需要准备一下的测试环境flash mx ColdFusionMX  源文件下载: 开始下载 10.1k  本文介绍ColdFusion和flash的通信方式,这次不使用Remoting组件,直接使用NetConnection对象,连接到ColdFusion的内置Remoting服务.该服务采用AMF结构的消息机制(动作消息格式).  请大家先温习一下NetConnection对象的方法和属性事件.new NetConnection()新建一个网络连接,connect方法用于连接到一个网络如果是rtmp协议则连接成功返回true,否则返回false,对于http的连接始终返回true.要判断连接是否成功,还可以使用onStatus事件.  close用于关闭连接. call用于调用一个服务,联机对象.call("服务",接收对象,参数列表,...)  现在: 打开flashmx新建文档 大小550*150 背景为淡青色.加为静态文字标签3个,动态文本框一个,输入文本框一个,打开共享库托入一个按钮::如下图所示:(为以上控件加入内容)  设置如下: 输入框 inputbox 按钮 button1 显示框 showbox  在第一帧加如下代码:代码拷贝框以下是引用片段://包含文件#include "NetDebug.as" 主要用于调试,并无其它意义,可选参数//System.useCodePage=true;当服务器不支持utf-8时候加如下代码.#include "NetDebug.as"conn=new NetConnection();//连接网关,在flashmx的窗口 service browser中可以找到.conn.connect("http://localhost:8500/flashservices/gateway");//呼叫服务//定义一个接收传回值的对象var obj={};//定义接收对象事件 onResult 当受到一个结果时候,此事件触发;obj.onResult=function (结果) {showbox.text = 结果;trace(结果);}obj.onStatus = function(info){trace("一个错误发生" + info.code);}function function1() {//使用命名参数传递对象,在参数比较多的时候,特别方便,推荐使用.var obj2={};obj2.sj=inputbox.text;conn.call("FLASHTEST.f2c",obj,obj2);}//定义按钮事件button1.onPress = function() {function1();}服务器代码编写:  新建站点:flashtest 新建文件f2c.cfm  代码如下 保存位置/flashtest/f2c.cfm<!--- 返回服务器的当前时间---><cfset theTime = timeFormat(now(), "h:mm:ss tt") ><!--- 特定的变量名 Flash.result可以回调客户端的.onresult ---><!--- 设定result的数据内容,#Flash.sj# 取得flash传递过来的实名数据变量.否则使用flash.params[1]获得变量,cf中数组从一开始,第一个参数为params[1]---><cfset flash.result = "欢迎" & #Flash.sj# & "使用全球领先的cf服务器系统,现在CF的标准时间是" & theTime>  执行的效果如下所示:
  • [Atlas200] 【启动】请问uart1 tx与rx短接口从内置flash启动后,默认ip是多少的?如何开启串口0打印?
    sd启动查询信息如下:uart1 tx rx短接口,启动到jump kernel没打印了:pc直连单板后,ping 192.168.0.2 无法ping通 。
  • [问题求助] “Flash download failed- Target DLL has been cancelled”
    【功能模块】STM32L431RC【操作步骤&问题现象】1、下载程序时显示如上问题2、任何其他设置都没有改动【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] 【智慧园区设施云】一文看懂Modbus, RTU, RS485等名词的联系
    Modbus rtu和Modbus tcp两个协议的本质都是MODBUS协议,都是靠MODBUS寄存器地址来交换数据;但所用的硬件接口不一样,Modbus RTU一般采用串口RS232C或RS485/422,而Modbus TCP一般采用以太网口。现在市场上有很多协议转换器,可以轻松的将这些不同的协议相互转换 如:Intesisbox可以把modbus rtu转换成Modbus tcp实际上Modbus协议包括ASCII、RTU、TCP。标准的Modicon控制器使用RS232C实现串行的Modbus。Modbus的ASCII、RTU协议规定了消息、数据的结构、命令和就答的方式,数据通讯采用Maser/Slave方式。Modbus协议需要对数据进行校验,串行协议中除有奇偶校验外,ASCII模式采用LRC校验,RTU模式采用16位CRC校验.ModbusTCP模式没有额外规定校验,因为TCP协议是一个面向连接的可靠协议。TCP和RTU协议非常类似,只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上5个0和一个6并通过TCP/IP网络协议发送出去即可 Modbus协议定义的寄存器地址是5位十进制地址,即:线圈(DO)地址:00000~09999触点(DI)地址:10000~19999输入寄存器(AI)地址:30000~39999输出寄存器(AO)地址:40000~49999由于上述各类地址是唯一对应的,因此有些资料就以其第一个数字区分各类地址,即:0x代表线圈(DO)类地址,1x代表触点(DI)类地址、 3x代表输入寄存器(AI)类地址、4x代表输出寄存器(AO)类地址。在实际编程中,由于前缀的区分作用,所以只需说明后4位数,而且需转换为4位十六进制地址。简单点说,modbus有四种数据,DI、DO、AI、AODI: 数字输入,离散输入,一个地址一个数据位,用户只能读取它的状态,不能修改。比如面板上的按键、开关状态,电机的故障状态。DO: 数字输出,线圈输出,一个地址一个数据位,用户可以置位、复位,可以回读状态,比如继电器输出,电机的启停控制信号。AI: 模拟输入,输入寄存器,一个地址16位数据,用户只能读,不能修改,比如一个电压值的读数。AO: 模拟输出,保持寄存器,一个地址16位数据,用户可以写,也可以回读,比如一个控制变频器的电流值。无论这些东西被叫做什么名字,其内容不外乎这几种,输入的信号用户只能看不能改,输出的信号用户控制,并可以回读。离散的数据只有一位,模拟的数据有16位。
  • [问题求助] 【XXX产品】【XXX功能】一句话描述问题
    【功能模块】【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [Atlas200] 【Atlas 200产品】【SPI功能】Atlas 200 SPI接口外面可以挂SPI flash吗?
    【功能模块】Atlas 200 SPI接口外面可以挂SPI flash吗?有片选信号0、1是可以挂两片spi flash吗?【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [Atlas200] 【A200 RC模式 SD卡启动】【dtb重新编译/flash升级】文档描述不一致,升级后查询版本未改变
    【功能模块】A200使用RC模式,通过SD卡启动。版本查询如下(dtb是重新编译并升级的),flash升级前后版本查询结果一样。升级指令: /var/davinci/driver/upgrade-tool --device_index -1 --component dtb --path /opt/mini/driver/dt.img【操作步骤&问题现象】1、升级只是影响到了main和backup分区,flash版本总是升级不上去,这个有影响吗?2、flash、main、backup三部分的版本不一致,以哪个为准?3、重新编译dtb使能UART0(修改hi1910-asic-1004.dts),参考文档描述不一致的问题。参考文档(https://support.huawei.com/enterprise/zh/doc/EDOC1100206814/99b1ae)【截图信息】Atlas-200-sdk_21.0.2.zip解压后,hi1910-asic-1004.dts中的描述如下:(红色部分是添加的使能UART0的,蓝色部分是描述不一致的地方)chosen {         bootargs = "root=/dev/mmcblk1p1 rw rootdelay=1 syslog no_console_suspend initrd=0x880004000,200M cma=356M@0x19800000 log_redirect=0x1fc000@0x6fe04000 default_hugepagesz=2M ascend_enable_all ascend_mini_enable"; }参考文档的描述如下:chosen { bootargs = "console=ttyAMA0,115200 root=/dev/mmcblk1p1 rw rootdelay=1 syslog no_console_suspend earlycon=pl011,mmio32,0x10cf80000 initrd=0x880004000,200M cma=256M@0x1FC00000 log_redirect=0x1fc000@0x6fe04000 default_hugepagesz=2M"; }【日志信息】(可选,上传日志内容或者附件)
  • [Atlas200] altas200模块spi读写有问题
    自制的altas200模块的底板,通过spi外设连接flash(w25q256),使用的是你们的官网例程:https://support.huawei.com/enterprise/zh/doc/EDOC1100193743/12390085设备驱动为//opt/source/kernel/linux-4.19/drivers/spi目录下的spidev.c,在读写flash的时候发现只要spi_ioc_transfer结构体的len设置超过64个字节,后面的好像就发不出去了,但是ioctl(fd,SPI_IOC_MESSAGE(1),&tx)函数还是会返回我设置的值后来使用示波器直接点SPI_CS0(gpio78)引脚发现每次发送字节数和这个引脚拉低的时间的对应关系为:1字节  9.6us10字节  81us50字节 400us64字节510us100字节 520us继续增大字节数时间基本变化不大所以想问问你们的内核里的spi主机驱动是不是有点问题?
  • [设备专区] msdk的flash读写函数是只读写内部的flash?
    【功能模块】读写flash功能【操作步骤&问题现象】尾端msdk的MSDK_FlashWrite/MSDK_FlashRead/MSDK_FlashErase是只操作内部的8M大小的flash?外部连接的flash要用spi去访问?【截图信息】无【日志信息】(可选,上传日志内容或者附件)无
  • [问题求助] 【200DK】【flash启动】200DK无法正常从flash启动
    【功能模块】atlas200 DK【操作步骤&问题现象】1、将UART1 TX与RX短接,上电启动2、启动停在了Start to jump Linux kernel3、sd卡可正常启动【截图信息】【日志信息】(可选,上传日志内容或者附件)456000002325152707178e005e00680pmu second pmu initial:0x3.cur[2], last[0].cur[3], last[0].0.5c-init ok[NVE] xloader version:0x3220cur[4], last[0].lp nve parameter avs enable 1!Ձmode 3, efuse:256!cpu pll recfg function,success---242978!cur[5], last[0].cur[6], last[0].cur[7], last[0].current[2].current[3].uc sector[0] valid flag not enabled!uc sector[1] valid flag not enabled![UC]ddr_iecc_en:0current[21].current[22].current[23].current[25].current[26].current[31].freq 60 vol 2:current[33].Density:0x120012Density:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0x120012Density:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0x120012Density:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0x120012Density:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0x120012Density:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0x120012Density:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0x120012Density:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0x120012Density:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffDensity:0xffffffffch_mask: 0xffch_lr: 0x3ch_num: 0x8rank_num: 0x1Manufacturer ID[0]:0x1Manufacturer ID[1]:0x1Manufacturer ID[2]:0x1Manufacturer ID[3]:0x1Manufacturer ID[4]:0x1Manufacturer ID[5]:0x1Manufacturer ID[6]:0x1Manufacturer ID[7]:0x1Total size:0x8DDR info:0x801DDR_TYPE_8CH_ALL 1_rank 8G !DDR HHA interleave enabled!idx: 0 start_addr_l: 0x0 start_addr_h: 0x0 length: 0x800idx: 1 start_addr_l: 0x80000000 start_addr_h: 0x8 length: 0x1800current[34].uce init entryuce profile total is 0x32cuce init endcurrent[35].freq 400 vol 2:freq 1600 vol 2:HVrefThres:8current[36].uce init entryuce profile total is 0x298uce init endcurrent[40].transfer ddr data to l2_buf 0x40000000.current[51].current[52].current[54].current[55].current[52].current[58].current[59].current[60].current[62].addr = d00c2030, value = 0addr = d00c3580, value = 0addr = d00c6320, value = 0addr = b0070e00, value = 0addr = d00c0a28, value = 0addr = b0070e30, value = 0addr = d00c0a58, value = 0addr = b0070e18, value = 0addr = d00c0a40, value = 0addr = b0072000, value = 3addr = d00c2540, value = 30addr = d00c00a0, value = 3faddr = d00c00a4, value = 3faddr = d00c0030, value = 241f41addr = d00c0034, value = 0addr = d00c0038, value = 241f41addr = d00c003c, value = 0addr = d00c5000, value = 3faddr = b0072004, value = 0addr = d00c2550, value = 0addr = d00c3690, value = 82current[63].DDR Total Capacity: 8192 MB.DDR Rank Type: single.DDR Channel with DRAM: A B C D E F G HDDR Init 994 mscurrent[65].current[67].cur[8], last[0].maintain:0, static:0cur[9], last[0].[STID]adcin7:0x0.[STID]adcin7_remap:0x0.[STID]adcin8:0x0.[STID]adcin8_remap:0x0.cur[10], last[0].SPI FLASH download uefi.cur[25], last[0].PCIE_RC_MODE...g_imgNum = 1cur[26], last[0].SPI FLASH download uefi finish.sicur[34], last[0].ns, succur[35], last[0].si succur[36], last[0].s vr bgns vr, sccur[37], last[0].sv sucsv suchigh mntn_config=0xa0000000, low mntn_config=0x0high addr=0x8, low addr=0x90000000lpm3 will WFEdisreset A55.cur[0], last[0].aicore_power_on:312----core1 step1aicore_pmu_up:233----repair step 5_aicore0_buck_on:164---state -0x7--cfg:0x6b--buck voltage:0x6b_aicore1_buck_on:222---state -0xf--cfg:0x6b--buck voltage:0x6b_aimemory0_1_buck_on:193---state -0xf--cfg:0x6b--buck voltage:0x6baicore_pmu_up:257----pmu up step1aicore0_power_reset:74aicore0_power_reset:83aicore1_power_reset:88aicore1_power_reset:97aicore1_isolation_enable:112----repair step 4aicore_power_on:331----power on success.DDR HHA interleave enabled!NOTICE:  Booting Trusted FirmwareNOTICE:  BL1: Built :NOTICE:  BL1: Booting BL2NOTICE:  BL2: Built :NOTICE:  BL1: Booting BL31NOTICE:  [bl31_early_platform_setup2] [59]NOTICE:  [bl31_early_platform_setup2] [59]NOTICE:  BL31: Built :NOTICE:  [hw_gic_setup] [90]booting cfg info 0x0, 0x0!start boot linux.(4551)booting cfg info 0x0, 0x0!Flash Start OS.Enter4BCmd(2078):ok!Exit4BCmd(2039):ok!NOTICE:  t-p12.booting cfg info 0x0, 0x0!C-dtbC-ImageC-filesystemCheck dtb hash ... MemCmp:7F, 7FCheck Image hash ... MemCmp:79, 79booting cfg info 0x0, 0x0!Check filesystem hash ... MemCmp:95, 95booting cfg info 0x0, 0x0![get_reserved_mem_size]:[1073L] memory total size[0x200000000], ddr dump size[0x0][get_reserved_mem]:[1147L] reserved memory invalid[0x890000000][0x0]Start to load LPM3.NOTICE:  t-p12.load LPM3 successfully.Start to jump Linux kernel(25692)
  • [技术干货] 固件(Firmware)概念
    固件是个统称,又称Firmware,硬件相关的都可以包含,是一种嵌入在硬件设备中的软件。固件(Firmware)就是写入EPROM(可擦写可编程只读存储器)或EEPROM(电可擦可编程只读存储器)中的程序。Firmware一般存储在ROM、EPROM、EEPROM、FLASH中,后面的EEPROM、FLASH都是电可擦除的。在以前,一般情况下是没有必要对固件进行升级操作的,即使在固件内发现了严重的Bug也必须由专业人员带着写好程序的芯片把原来机器上的更换下来。早期固件芯片一般采用了ROM设计,它的Firmware代码是在生产过程中固化的,用任何手段都无法修改。随着技术的不断发展,修改固件以适应不断更新的硬件环境成了用户们的迫切要求,所以,可重复写入的可编程可擦除只读存储器EPROM(Erasable Programmable ROM)、EEPROM和FLASH出现了。这些芯片是可以重复刷写的,让固件得以修改和升级,升级固件也变得越来越简单。固件担任着一个系统最基础最底层工作的软件。而在硬件设备中,固件就是硬件设备的灵魂,因为一些硬件设备除了固件以外没有其它软件组成,因此固件也就决定着硬件设备的功能及性能。服务器中最典型的固件就是BIOS、BMC。
总条数:27 到第
上滑加载中