Microcontrôleur STM32 débutant 8-SPI flash (W25Q128) lecture et écriture de données

        Lors de l'utilisation d'un micro-ordinateur à puce unique pour le développement de projets, une grande quantité de données doit être stockée (par exemple, si un écran est utilisé comme dispositif d'affichage, il est souvent nécessaire de stocker des données telles que des images et des animations), le Flash à l'intérieur du micro-ordinateur monopuce n'est souvent pas suffisant.

e77259fc7eab4b94bd8719616f845685.png

        Par exemple, dans la série STM32F103, le flash interne ne peut atteindre au maximum que 512 Ko. En supposant qu'une résolution de 240*240 et des images couleur de 64K doivent être stockées, il suffit de stocker environ 4 images. Si une mémoire externe est utilisée, d'autres données telles que des images sont placées dans la mémoire externe et le flash interne ne stocke que les programmes, ce qui peut réduire la demande de flash interne et réduire les coûts.     f89a1e2ee7ce46f29538d0aae0a794ad.png         Il existe de nombreux types de Flash, qui peuvent être divisés en Flash NOR et Flash NAND en fonction de leurs différents procédés de fabrication et procédés de fabrication. La structure série de NAND permet d'augmenter facilement sa capacité (les cartes SD, les disques U et les disques durs utilisent principalement ce type de Flash), mais sa vitesse de lecture n'est pas aussi bonne que celle de NOR Flash avec une structure parallèle, et sa fiabilité est moins bonne. , une fois qu'un mauvais point dans un bloc de données se produit, il est irréversible et irréparable. En raison de son principe de stockage de données, Flash doit effacer le bloc où se trouve l'adresse de données avant d'écrire de nouvelles données.La vitesse d'effacement de NOR Flash est beaucoup plus lente que celle de NAND.

7228c611da7b43a09476edf490316aca.png

        Flash peut être divisé en transmission par port parallèle et transmission par port série selon les différentes méthodes de transmission de données. La transmission du port parallèle de STM32 doit utiliser l'interface FSMC.Bien que sa vitesse de lecture et d'écriture soit très rapide, il n'a pas de fonction FSMC pour les paquets inférieurs à 100PIN.

f8cd3d8f0c364191a38e40f2d5ee3759.png

         Utilisez donc plus de transmission par port série. La méthode du port série adopte généralement la communication SPI.

6c9a5a1a084749749b291b19d04c2b99.png

          La série W25Q Flash est la série SPI Flash produite par Winbond (Taiwan Winbond Technology), et il s'agit d'un Flash externe relativement couramment utilisé dans le développement de micro-ordinateurs à puce unique. Il prend en charge les standards SPI à quatre fils, Dual SPI, Quad SPI et QPI, et sa fréquence d'horloge peut atteindre 104 MHz, 208 MHz et 416 MHz respectivement. Pour la série STM32F103, sa fréquence principale est jusqu'à 72 MHz (le taux de communication SPI est jusqu'à 18 Mbps), de sorte que le SPI standard est suffisant pour le MCU de la série F103. Ici, j'utilise W25Q128FV pour expliquer l'utilisation de Flash.

b29518b923934ed3bb13dcceccd20530.png

        Commençons par comprendre sa définition de broche. Celle illustrée ci-dessus est le package SOP8 et le package SOP16. Les fonctions sont similaires.

ac7b949edaa94de1a6d71992a34c5903.png        /CS : broche de sélection de puce de Flash. Lorsque /CS est élevé, les broches de sortie de données série (DO ou IO0, IO1, IO2, IO3) de Flash sont en haute impédance, et la consommation d'énergie de l'appareil sera au niveau de veille (à moins que l'effacement interne, la programmation ou l'écriture est en cours) cycle de registre d'état). Lorsque /CS est bas, Flash sera sélectionné, la consommation d'énergie augmentera jusqu'au niveau actif et les instructions pourront être écrites et lues à partir de l'appareil. Après le démarrage, /CS doit passer du niveau haut au niveau bas pour accepter de nouvelles commandes.

        DO(IO1) : DO fait référence au port de sortie de données (Data Out), qui est généralement connecté au port d'entrée de données de l'interface SPI du microcontrôleur, à savoir MISO. IO1 est sa fonction de multiplexage.Lorsque le mode de transmission SPI à quatre bits est activé, la fonction de broche est IO1.

        /WP : broche de protection en écriture (WP). Peut être utilisé pour empêcher l'écriture du registre d'état. Avec les bits de protection de bloc du registre d'état (CMP, SEC, TB, BP2, BP1 et BPO) et le bit de protection du registre d'état (SRP), des secteurs aussi petits que 4 Ko ou l'ensemble de la matrice de mémoire peuvent être protégés matériellement. La broche /WP est active bas . Lorsque le bit QE du registre d'état 2 est défini sur Quad I/O, la fonction de broche /WP n'est pas disponible car cette broche est utilisée pour IO2. Si vous ne souhaitez pas utiliser cette fonction, vous pouvez directement connecter cette broche à VCC.

        GND : alimentation Flash GND

        DI : DI fait référence au port d'entrée de données (Data In), qui est généralement connecté au port de sortie de données de l'interface SPI du microcontrôleur, à savoir MOSI. IO0 est sa fonction de multiplexage, lorsque le mode de transmission SPI à quatre bits est activé, cette broche est IO0

        CLK : ligne d'horloge SPI. Connectez-vous à l'interface d'horloge SPI du microcontrôleur.

        /HOLD, /RESET : / HOLD permet à l'appareil de suspendre activement la transmission de données. Lorsque /HOLD est bas et /CS est bas, la broche DO sera en haute impédance et les signaux sur les broches Dl et CLK seront ignorés. Lorsque /HOLD est activé, l'appareil peut reprendre son fonctionnement. /HOLD est utile lorsque plusieurs appareils partagent le même signal SPl. La broche /RESET est utilisée pour la réinitialisation de l'appareil. Notez que s'il est réinitialisé lors de l'écriture de données, cela peut entraîner une perte de données. Par conséquent, s'il n'est pas nécessaire de réinitialiser le Flash, cette broche est souvent directement connectée au VCC.

        VCC : Alimentation Flash 3.3V.

7eba7f0d565740ef9855618baaec886f.png

         Quel que soit le type de mémoire, il est nécessaire de connaître l'adresse des données lors de la lecture et de l'écriture de données. Les données sont stockées dans des registres, de sorte que l'adresse des données est l'adresse du registre. Jetons un coup d'œil au schéma interne du W25Q128.

aa61ec1836eb43cc8abd2057bcae6def.jpeg

        Son intérieur est composé d'une unité de stockage de données et de divers contrôleurs.

        L'unité minimale d'une unité de stockage est un registre, et chaque registre peut stocker 1 octet de données.

        Tous les 256 registres forment une page (Page), c'est-à-dire qu'une page peut stocker des données de 256 octets,

        Toutes les 16 pages forment un secteur (Sector), et un secteur peut stocker des données de 16x256=4096Byte (environ 4KB). Par exemple, la plage d'adresses de données du secteur 0 est 000000h-000FFFh.

        Tous les 16 secteurs forment un bloc (Bloc), et un bloc peut stocker des données de 4096x16=65536Byte (environ 64K). Par exemple, la plage d'adresses de données du bloc 0 est 000000h-00FFFFh.

         L'unité de stockage entière a un total de 256 blocs, donc sa capacité de stockage totale est de 256x65536=16777216Byte data, soit environ 16MByte. La plage d'adresses de données est 000000h-FFFFFFh.


        Peu importe le type de périphériques contrôlés par l'envoi de commandes et de données. Flash ne fait pas exception, vous devez donc savoir utiliser Flash, il vous suffit de trouver sa table de commandes dans son manuel technique.

1eb7b096d730401f8731f166f9f44c0c.png

2c843b15728d4898999946054b316765.png

         Il existe de nombreuses commandes disponibles, mais seules quelques-unes sont couramment utilisées.

        Expliquons maintenant comment implémenter STM32F103 pour lire et écrire des données SPI Flash dans le programme.

        Ici, j'utilise SPI2, la connexion matérielle est la suivante.

ae033bf93f1543dbb2b391aa553c6bb2.png

        Reprenons la partie programme :

1. Initialisation SPI2. Afin d'afficher les données lues, j'utilise ici le port série pour transférer les données vers l'ordinateur. Initialisez donc également usart1.

void SPI2_UserInit(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//使能GPIOB的时钟

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15;    //PA13为SCK时钟,PA15为MISO
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//速度50MHz
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			//复用推挽输出
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;					//PA14为MISO
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			    //速度50MHz
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;			//浮空输入
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;					//PA12为片选
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			    //速度50MHz
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;				//复用推挽输出
  GPIO_Init(GPIOB, &GPIO_InitStructure);
 
  SPI_InitTypeDef SPI_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);		     //使能SPI时钟
	
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//设置双向双线全双工
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;						//设置为SPI主站
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;					//设置为8位帧结构
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;					//串行时钟的稳态为时钟高
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;		//位捕获的时钟活动沿为第1个时钟沿
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;					//指定NSS信号由软件控制
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;    //波特率预分频值
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;					//数据位从MSB开始
  SPI_InitStructure.SPI_CRCPolynomial = 7;								//CRC检验
  SPI_Init(SPI2, &SPI_InitStructure);							//按以上设置初始化SPI2
 
  SPI_Cmd(SPI2, ENABLE);							//使能SPI2
  GPIO_SetBits(GPIOB,GPIO_Pin_12);	            	//CS置高
}

void USART1_Userinit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;    //PA9为USART1_TX将这个GPIO初始化
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//速度50MHz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;				//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;	//PA10为USART_RX,将这个GPIO初始化
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//速度50MHz
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;		//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);
		
	USART_InitTypeDef USART_InitStructure;						//定义USART配置结构体
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);		//打开USART1时钟
	
	USART_InitStructure.USART_BaudRate = 115200; 					//波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//数据帧位数 
	USART_InitStructure.USART_StopBits = USART_StopBits_1; 			//停止位数目
	USART_InitStructure.USART_Parity = USART_Parity_No; 			//奇偶模式(USART_Parity_No 无,USART_Parity_Even 偶SART_Parity_Odd奇)
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 	//硬件流控制模式
	USART_InitStructure.USART_Mode = USART_Mode_Tx| USART_Mode_Rx; 									//发送、接收使能
	USART_Init(USART1, &USART_InitStructure);											//初始化
	USART_Cmd(USART1,ENABLE);																			//使能USART1串口	
	
	USART_ITConfig(USART1,USART_IT_RXNE, ENABLE);		//使能USART1接收中断
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;				//中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;	//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;				//子优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//使能中断
	NVIC_Init(&NVIC_InitStructure);									//初始化中断
	
}

2. L'envoi de commandes ou la lecture et l'écriture de données sont réalisés via les fonctions les plus élémentaires d'envoi et de réception de données. Afin de ne pas causer de perte de données, il est nécessaire de déterminer si les données envoyées la dernière fois ont été envoyées avant d'envoyer des données à chaque fois, ce qui peut être jugé par les bits d'indicateur pertinents ; de même, afin d'éviter la duplication des données, il est nécessaire pour juger de la réception avant de recevoir des données à chaque fois Si le tampon est vide.

96abddd3019e4c1ab73db248307031c2.png

void Flash_WriteData8(u8 Data)	//写8位数据(1个字节)
{
	u8 Wait=0;	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET&&Wait<20) //检查指定的SPI标志位设置与否:发送缓存空标志位,RESET表示正在发送数据
	{
		Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待
	}
	SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_TXE);    //清除发送完成标志位
	SPI_I2S_SendData(SPI2,Data);				//发送Data
	
}

u8 Flash_ReadData( )														//读一个字节
{		
	u8 Wait=0;
	SPI_I2S_SendData(SPI2,0xff);									//发送0x00,产生时钟信号,用来接收数据,也可以发送其他无响应的命令
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET&&Wait<200) //检查指定的SPI标志位设置与否:接受缓存非空标志位
	{
		Wait++;	
	}	
	SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_RXNE);	//清除标志位
	return SPI_I2S_ReceiveData(SPI2);						 //返回通过SPI2最近接收的数据			
}

        Remarque : Dans la fonction de réception de données, la raison pour laquelle la fonction SPI_I2S_SendData(SPI2,0xff) est utilisée avant la fonction SPI_I2S_ReceiveData(SPI2) est de générer un signal d'horloge.

        SPI adopte une structure de communication maître-esclave.Le signal d'horloge ne peut être généré que par l'appareil maître.L'appareil maître générera un signal d'horloge pendant le processus d'envoi de données, mais l'appareil esclave ne peut pas générer de signal d'horloge par lui-même lors de l'envoi de données , il ne peut donc pas transférer les données d'un bit. Un bit est envoyé (la communication synchrone doit s'appuyer sur le signal d'horloge pour maintenir la cohérence de la synchronisation), puis seul le périphérique maître peut être invoqué pour générer le signal d'horloge. Le 0xFF envoyé par l'appareil maître est une donnée invalide pour l'appareil esclave et ne répondra pas aux données, mais lorsque l'appareil maître envoie des données 0xFF, un signal d'horloge est généré, de sorte que l'appareil esclave s'appuie sur ce signal d'horloge, les données sont envoyé au dispositif maître, et le dispositif maître l'accepte et le stocke temporairement dans le registre tampon de réception, et met automatiquement à jour le registre lorsque de nouvelles données sont reçues.

6775cad804074e1ab4427003d33ff4eb.png

         Pour une synchronisation de communication spécifique, veuillez vous référer au manuel technique de W25Q128 ( W25Q128FV_PDF_Datasheet_Datasheet ), qui ne sera pas répertorié ici.

3. Encapsulez les commandes couramment utilisées de W25Q128 dans des fonctions, tant que les fonctions correspondantes sont appelées, l'envoi de commandes et la lecture et l'écriture de données peuvent être réalisées

#define Flash_CS_H() GPIO_SetBits(GPIOB,GPIO_Pin_12)		//Flash 片选信号
#define Flash_CS_L() GPIO_ResetBits(GPIOB,GPIO_Pin_12)      //低电平选中,高电平取消选中

/*****W25Q128常用命令定义*****/
#define W25X_WriteEnable		0x06 		//写使能
#define W25X_WriteDisable		0x04 		//写失能
#define W25X_ReadStatusReg		0x05 		//读控制寄存器
#define W25X_WriteStatusReg		0x01 		//写控制寄存器
#define W25X_ReadData			0x03 		//写数据
#define W25X_FastReadData		0x0B 		//快速写数据
#define W25X_FastReadDual		0x3B 		//
#define W25X_PageProgram		0x02 		//页编程
#define W25X_BlockErase32		0x52 		//32K块擦除
#define W25X_BlockErase64		0xD8 		//64K块擦除
#define W25X_SectorErase		0x20 		//4k扇区擦除
#define W25X_ChipErase			0xC7 		//整片擦除
#define W25X_PowerDown			0xB9 		//
#define W25X_ReleasePowerDown	0xAB 	
#define W25X_DeviceID			0xAB 		//读设备ID
#define W25X_ManufactDeviceID	0x90 
#define W25X_JedecDeviceID		0x9F		//读取JEDECID

u8 Flash_ReadSR(void)   									//读状态寄存器
{  
	u8 Byte=0;
	Flash_CS_L();        //CS选中
	Flash_WriteData8(W25X_ReadStatusReg); 	//发送读取状态寄存器命令    
	Byte=Flash_ReadData(); //读取一个字节 
	Flash_CS_H();        //CS取消选中
	return Byte;   
} 
 
void Flash_Write_SR(u8 Sr)   
{                               
	Flash_WriteData8(W25X_WriteStatusReg);	//发送写状态寄存器命令    
	Flash_WriteData8(Sr);               		//写入一个字节                             
}   
 
void Flash_Write_Enable(void)   					//使能写入
{ 
	Flash_CS_L();
  Flash_WriteData8(W25X_WriteEnable); 		//发送写使能
	Flash_CS_H();	
} 
 
void Flash_Write_Disable(void)  		 			//禁止写入
{                     
    Flash_WriteData8(W25X_WriteDisable);  //发送写禁止指令                             
}



u32 Flash_ReadID(void)				//读取设备ID
{
	u32 Temp ;	 
	u8 TempL,TempM,TempH;	
	Flash_CS_L();
	Flash_WriteData8(W25X_JedecDeviceID);								//发送读取ID命令	    
	TempH=Flash_ReadData();			//接收高8位
	TempM=Flash_ReadData();			//接收中8位
	TempL=Flash_ReadData();			//接收低8位
	Temp=TempH;
	Temp<<=8;										//左移8位
	Temp|=TempM;								//高8位与低8位合并成16位(与运算后赋值)
	Temp<<=8;										//左移8位
	Temp|=TempL;								//高8位与低8位合并成16位(与运算后赋值)
	Flash_CS_H();
	return Temp;
} 
void Flash_ReadSector(u32 ReadAddr)  //读一个扇区
{
	u16 i;   										                              
  Flash_WriteData8(W25X_ReadData);         	//发送读取命令   
  Flash_WriteData8((ReadAddr>>16)&0xff);  	//发送24bit地址    
  Flash_WriteData8((ReadAddr>>8)&0xff);   
  Flash_WriteData8(ReadAddr&0xff);   
  for(i=0;i<4096;i++)				//一个扇区4096个Byte
	{ 
     ARR2[i]=Flash_ReadData();   	//循环读取每个字节
  }
}

void Flash_EraseSector(u32 Dst_Addr)   		//擦除扇区
{  	  
  Flash_Write_Enable();                  	//SET WEL 	 
  while((Flash_ReadSR()&0x01)==0x01);  		// 等待BUSY位清空
	{
		
	}
  Flash_WriteData8(W25X_SectorErase);      	//发送扇区擦除指令 
  Flash_WriteData8((Dst_Addr>>16)&0xff);  	//发送24bit地址    
  Flash_WriteData8((Dst_Addr>>8)&0xff);   
  Flash_WriteData8(Dst_Addr&0xff); 
	while((Flash_ReadSR()&0x01)==0x01);  		// 等待BUSY位清空
	{
		
	}
} 

void Flash_WritePage(u32 WriteAddr,u8 NumByteToWrite)
{
	u8 i;  
	Flash_Write_Enable();                  		//写使能 
	Flash_CS_L();
	Flash_WriteData8(W25X_PageProgram);      	//发送写页命令   
	Flash_WriteData8((WriteAddr>>16)&0xff); 	//发送24bit地址    
	Flash_WriteData8((WriteAddr>>8)&0xff);   
	Flash_WriteData8(WriteAddr&0xff);   
	for(i=0;i<NumByteToWrite;i++)
	{
		Flash_WriteData8(ARR1[i]);
	} 
    Flash_CS_H();
}

void Flash_WriteByte(u32 WriteAddr,u8 Data)		//写入一个字节
{
	Flash_Write_Enable();                  		//写使能 
	Flash_CS_L();
	Flash_WriteData8(W25X_PageProgram);      	//发送写页命令   
	Flash_WriteData8((WriteAddr>>16)&0xff); 	//发送24bit地址    
	Flash_WriteData8((WriteAddr>>8)&0xff);   
	Flash_WriteData8(WriteAddr&0xff);   
	Flash_WriteData8(Data); 
    Flash_CS_H();
}

4. Fonction principale. Lisez d'abord l'ID de l'appareil, puis écrivez les données du tableau ARR1 dans le secteur 0 du Flash, puis lisez les données du secteur 0 et placez-les dans le tableau ARR2, et affichez les données d'ARR2 sur l'ordinateur via le port série port (assistant de débogage de port série) .

#include<stm32f10x.h>

u8 ARR1[10]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A};//随意填入几个元素,后面将其元素写入Flash中
u8 ARR2[10];    //后面将Flash的数据读出来,复制到该数组中

void USART_SendDatatoUSB( char ASCII[])    //串口发送字符串函数
{
	u8 i,j,Wait;
	for(i=0;i<12;i++)
	{
		u8 Wait=0;				 	
		while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200) 
		{
			Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待
		}
		USART_ClearFlag(USART1,USART_FLAG_TC);
		j=ASCII[i];
		USART_SendData(USART1,j);
	}
	Wait=0;
	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200) 
	{
		Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待
	}
	USART_ClearFlag(USART1,USART_FLAG_TC);
	USART_SendData(USART1,10);	//换行	
}

int main()
{
    SPI2_UserInit();			//SPI2初始化----控制SPI Flash
    USART1_Userinit();			//USART1初始化--控制串口CH340
    
    Flash_Write_Enable();    //Flash写使能
    USART_SendDatatoUSB( "Flash:ID")
	
	Data=Flash_ReadID();    //读取ID(16位)
	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
    {

     }
	USART_SendData(USART1,(Data>>16)&0xFF);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
    {

     }
	USART_SendData(USART1,(Data>>8)&0xFF);

	Flash_EraseSector(0x000000);        //擦除扇区
    USART_SendDatatoUSB( "扇区擦除完成");
    Flash_WritePage(0x000000,10);        //写入数据
    USART_SendDatatoUSB( "数据写入成功");
    
    Flash_ReadSector(0x000000)  //读数据
    USART_SendDatatoUSB( "数据读取成功");
    

	for(i=0;i<10;i++)
	{
		u8 Wait=0;				 	
		while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200) //检查指定的SPI标志位设置与否:发送缓存空标志位,RESET表示正在发送数据
		{
			Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待
		}
		USART_ClearFlag(USART1,USART_FLAG_TC);
		j=ARR2[i];
		USART_SendData(USART1,j);
	}
}

Enfin reçu les données comme suit.

8b7a885b40e84beba5e27800be20b684.png

Je suppose que tu aimes

Origine blog.csdn.net/qq_55203246/article/details/129032117
conseillé
Classement