网站到期只续域名不续空间能打开吗/seo工资水平

网站到期只续域名不续空间能打开吗,seo工资水平,怎么看网站是什么时候做的,中国电商平台有多少家【STM32】通过HAL库Flash建立FatFS文件系统并配置为USB虚拟U盘MSC 在先前 分别介绍了FatFS文件系统和USB虚拟U盘MSC配置 前者通过MCU读写Flash建立文件系统 后者通过MSC连接电脑使其能够被操作 这两者可以合起来 就能够实现同时在MCU、USB中操作Flash的文件系统 【STM32】通过…

【STM32】通过HAL库Flash建立FatFS文件系统并配置为USB虚拟U盘MSC

在先前 分别介绍了FatFS文件系统和USB虚拟U盘MSC配置
前者通过MCU读写Flash建立文件系统 后者通过MSC连接电脑使其能够被操作
这两者可以合起来 就能够实现同时在MCU、USB中操作Flash的文件系统
【STM32】通过L496的HAL库Flash建立FatFS文件系统(CubeMX自动配置R0.12C版本)
【STM32】HAL库USB虚拟U盘MSC配置及采用自带的Flash作为文件系统

在这里 USB还是工作在Device模式 而不是Host模式 如果配置成Host模式则可以解锁FatFS中的USB Disk功能 实现方式不同 原理、功能也不一样 请勿混淆
(在Host模式下 MCU作为主机使用 需要多加一根线 USB Disk功能需要连上USB以后才能挂载硬盘 其相关配置可以由CubeMX完全生成 用户只需要调用文件操作函数API即可 不需要修改代码 感兴趣的可以自己尝试一下相关配置)

文章目录

  • Flash操作
    • Flash地址写
    • Flash地址读
  • FatFS文件系统配置
  • FatFS移植
    • 驱动函数
    • 时间戳函数
  • 文件操作函数
    • 工作区缓存
    • 文件挂载和格式化测试
    • 文件读写测试
    • 其他文件操作函数
  • MSC配置
  • 工程配置
  • 测试
  • 附录:Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作
    • SysTick系统定时器精准延时
      • 延时函数
        • 阻塞延时
        • 非阻塞延时
    • 位带操作
      • 位带代码
        • 位带宏定义
        • 总线函数
      • 一、位带操作理论及实践
      • 二、如何判断MCU的外设是否支持位带

Flash操作

无论是何种Flash 都能进行读写操作
读一般可以随机地址读取 但写操作只能按某一个最小单位进行擦除后 才能写入
【STM32】HAL库Flash读写操作及配置(L4和F4系列不同操作、HAL_FLASH_ERROR_PGA报错的解决方案)
为了能够用自带的Flash进行文件系统的建立 首先空间不能太小
其次 为了方便编程 可以选择多页面、小空间的Flash进行操作
若采用F407 每次写入擦除的最小单位是一个扇区(128K) 编程起来比较麻烦
所以本文采用L496来进行操作

这里我们就用496的第二个BANK来作为硬盘操作(地址0x0808 0000 之后的数据 总共256页 每页2K大小 总大小512K)
在这里插入图片描述

操作L496的话 是双字64位操作
在这里插入图片描述
在双Bank模式下 每次擦除时还需要选择擦除的Bank序号(1或2 或两者都擦除)

/** @defgroup FLASH_Banks FLASH Banks* @{*/
#define FLASH_BANK_1              ((uint32_t)0x01)                          /*!< Bank 1   */
#if defined (STM32L471xx) || defined (STM32L475xx) || defined (STM32L476xx) || defined (STM32L485xx) || defined (STM32L486xx) || \defined (STM32L496xx) || defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || defined (STM32L4R5xx) || \defined (STM32L4R7xx) || defined (STM32L4R9xx) || defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
#define FLASH_BANK_2              ((uint32_t)0x02)                          /*!< Bank 2   */
#define FLASH_BANK_BOTH           ((uint32_t)(FLASH_BANK_1 | FLASH_BANK_2)) /*!< Bank1 and Bank2  */
#else
#define FLASH_BANK_BOTH           ((uint32_t)(FLASH_BANK_1))                /*!< Bank 1   */
#endif

HAL库测试代码如下:

void Test_Flash(uint32_t add)
{uint32_t error = 0;uint64_t dat = 0x0123456776543210;//要写入的数据,必须得是双字64bituint64_t read_dat = 0 ;FLASH_EraseInitTypeDef flash_dat;          //定义一个结构体变量,里面有擦除操作需要定义的变量HAL_FLASH_Unlock();                                    //第二步:解锁                        flash_dat.TypeErase = FLASH_TYPEERASE_PAGES;         //擦除类型是“Page Erase” 仅删除页面 另外一个参数是全部删除flash_dat.Page = (uint32_t)((add-0x08000000)/2048);            //擦除地址对应的页flash_dat.NbPages = 1;                               //一次性擦除1页,可以是任意页if(flash_dat.Page>255){flash_dat.Banks=2;}else{flash_dat.Banks=1;}HAL_FLASHEx_Erase(&flash_dat,&error);            //第三步:参数写好后调用擦除函数FLASH_WaitForLastOperation(0xFFFF); HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, add, dat);//第四步:写入数据HAL_FLASH_Lock();                                      //第五步:上锁read_dat = *(__I uint64_t *)add;	   //读出flash中的数据uint32_t read_dat1=read_dat>>32;uint32_t read_dat2=read_dat&0x00000000FFFFFFFF;printf("[INFO] Flash_Test:0x%08x 0x%08x\n",read_dat1,read_dat2);
}

步骤就是:
解锁;
擦除;
写数据;
上锁。

若要在写入某个地址下的一部分数据时 需要擦除整个页面 然后再进行写入
所以如果要保留该页面下的其他数据 就应该在写入之前读取该页面数据 然后将某一部分修改的数据替换掉
之后再按页面整个写入
好在文件系统中 只要配置得当 可以帮我们实现按页擦除、写入的功能
这样我们就只需要定义好地址写、地址读函数即可

这里需要注意 由于L496的Flash是按64位对其 而我们的MCU是32位 所以不建议直接进行64位移位操作
最好是用两个32位变量 来拼接成一个64位
并且需要注意的是 32位变量左移位时 不得操作32位 最好是先赋值给64位变量 再单独对64位变量进行操作
同理 在读取函数中 64位变量也建议拆分成两个32位变量进行读取操作

Flash地址写

//读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToWrite:要读取的字节数(最大65535)
void Write_Flash(const uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{if(Flag_Flash_Busy==1)return;Flag_Flash_Busy=1;uint32_t Current_ADD = ReadAddr;	uint32_t add =0;uint32_t page=(uint32_t)((Current_ADD-0x08000000)/2048);uint32_t first_add = Current_ADD;	uint32_t judg_add = (page)*0x800+0x08000000+Flash_Page_Size;uint32_t error = 0;uint64_t dat = 0;														//要写入的数据,必须得是双字64bituint32_t dat_0=0;uint32_t dat_1=0;uint16_t i =0;uint16_t j = NumByteToRead/8;FLASH_EraseInitTypeDef flash_dat;          //定义一个结构体变量,里面有擦除操作需要定义的变量HAL_FLASH_Unlock();                                    //第二步:解锁                        flash_dat.TypeErase = FLASH_TYPEERASE_PAGES;         //擦除类型是“Page Erase” 仅删除页面 另外一个参数是全部删除flash_dat.Page = (uint32_t)((Current_ADD-0x08000000)/2048);            //擦除地址对应的页flash_dat.NbPages = 1;                               //一次性擦除1页,可以是任意页if(flash_dat.Page>255){flash_dat.Banks=2;}else{flash_dat.Banks=1;}HAL_FLASHEx_Erase(&flash_dat,&error);            //第三步:参数写好后调用擦除函数FLASH_WaitForLastOperation(0xFFFF); for(i=0;i<j;i++){add = Current_ADD+i*8;if(add>=judg_add){HAL_FLASH_Lock();     //第五步:上锁Flag_Flash_Busy=0;Write_Flash(pBuffer+i*8,add-first_add,NumByteToRead-i*8);return;}dat_0 = pBuffer[i*8+0]|(pBuffer[i*8+1]<<8)|(pBuffer[i*8+2]<<16)|(pBuffer[i*8+3]<<24);		dat_1 = pBuffer[i*8+4]|(pBuffer[i*8+5]<<8)|(pBuffer[i*8+6]<<16)|(pBuffer[i*8+7]<<24);dat = dat_1;dat = (dat<<32)|dat_0;HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, add, dat);  //第四步:写入数据}HAL_FLASH_Lock();     //第五步:上锁Flag_Flash_Busy=0;
}

Flash地址读

//读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void Read_Flash(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{if(Flag_Flash_Busy==1)return;Flag_Flash_Busy=1;uint32_t Current_ADD = ReadAddr;	uint32_t add =0;uint64_t dat = 0;														//要写入的数据,必须得是双字64bituint32_t dat_0=0;uint32_t dat_1=0;uint16_t i =0;uint16_t j = NumByteToRead/8;for(i=0;i<j;i++){add = Current_ADD+i*8;dat = *(__I uint64_t *)(add);dat_1=dat>>32;dat_0=dat&0x00000000FFFFFFFF;pBuffer[i*8+0]=(uint8_t)(dat_0&0xFF);pBuffer[i*8+1]=(uint8_t)((dat_0>>8)&0xFF);pBuffer[i*8+2]=(uint8_t)((dat_0>>16)&0xFF);pBuffer[i*8+3]=(uint8_t)((dat_0>>24)&0xFF);pBuffer[i*8+4]=(uint8_t)(dat_1&0xFF);pBuffer[i*8+5]=(uint8_t)((dat_1>>8)&0xFF);pBuffer[i*8+6]=(uint8_t)((dat_1>>16)&0xFF);pBuffer[i*8+7]=(uint8_t)((dat_1>>24)&0xFF);}Flag_Flash_Busy=0;
}

FatFS文件系统配置

FatFS文件系统依赖底层Flash驱动来进行文件系统配置
通过实现f_open等函数操作来进行文件的操作
这里就不讲解底层原理了 相关资料很多
可以通过CubeMX进行配置
如图:
在这里插入图片描述
修改以支持中文字符
修改MAX_SS为2048(496的一个页面是2K)
这里MAX_SS只能选择512 1024 2048 4096 其对应的就是格式化中的“分配单元大小”
也就是规定其最小操作单元为2048

另外 配置好RTC(可用可不用)
在这里插入图片描述

FatFS移植

CubeMX生成代码后 需要在工程中进行配置
导入用户文件:
在这里插入图片描述
导入外设中的FatFS库文件
在这里插入图片描述
添加头文件目录:
在这里插入图片描述

驱动函数

修改user_diskio.c中的函数:

/*** @brief  Initializes a Drive* @param  pdrv: Physical drive number (0..)* @retval DSTATUS: Operation status*/
DSTATUS USER_initialize (BYTE pdrv           /* Physical drive nmuber to identify the drive */
)
{/* USER CODE BEGIN INIT */Stat = STA_NOINIT;//获取驱动器状态Stat = USER_status(pdrv);  return Stat;/* USER CODE END INIT */
}/*** @brief  Gets Disk Status* @param  pdrv: Physical drive number (0..)* @retval DSTATUS: Operation status*/
DSTATUS USER_status (BYTE pdrv       /* Physical drive number to identify the drive */
)
{/* USER CODE BEGIN STATUS */Stat = STA_NOINIT;		  //驱动器未初始化,Stat=0x01Stat = 0 ;  //Stat=0x00return Stat;/* USER CODE END STATUS */
}/*** @brief  Reads Sector(s)* @param  pdrv: Physical drive number (0..)* @param  *buff: Data buffer to store read data* @param  sector: Sector address (LBA)* @param  count: Number of sectors to read (1..128)* @retval DRESULT: Operation result*/
DRESULT USER_read (BYTE pdrv,      /* Physical drive nmuber to identify the drive */BYTE *buff,     /* Data buffer to store read data */DWORD sector,   /* Sector address in LBA */UINT count      /* Number of sectors to read */
)
{/* USER CODE BEGIN READ */uint32_t globalAddr = (sector)*0x800+0x08080000;  uint16_t byteCount = count << 11;   //读取数据Read_Flash((uint8_t *)buff,globalAddr, byteCount);return RES_OK;/* USER CODE END READ */
}/*** @brief  Writes Sector(s)* @param  pdrv: Physical drive number (0..)* @param  *buff: Data to be written* @param  sector: Sector address (LBA)* @param  count: Number of sectors to write (1..128)* @retval DRESULT: Operation result*/
#if _USE_WRITE == 1
DRESULT USER_write (BYTE pdrv,          /* Physical drive nmuber to identify the drive */const BYTE *buff,   /* Data to be written */DWORD sector,       /* Sector address in LBA */UINT count          /* Number of sectors to write */
)
{/* USER CODE BEGIN WRITE *//* USER CODE HERE */uint32_t globalAddr = (sector)*0x800+0x08080000;  uint16_t byteCount = count << 11;   Write_Flash((uint8_t *)buff,globalAddr, byteCount);return RES_OK;/* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 *//*** @brief  I/O control operation* @param  pdrv: Physical drive number (0..)* @param  cmd: Control code* @param  *buff: Buffer to send/receive control data* @retval DRESULT: Operation result*/
#if _USE_IOCTL == 1
DRESULT USER_ioctl (BYTE pdrv,      /* Physical drive nmuber (0..) */BYTE cmd,       /* Control code */void *buff      /* Buffer to send/receive control data */
)
{/* USER CODE BEGIN IOCTL */DRESULT res = RES_OK;switch(cmd){/*以下四个命令都是按照FatFs默认参数配置时必须需要的*///完成挂起的写入过程(在_FS_READONLY == 0时需要)case CTRL_SYNC:               //确保设备已完成挂起的写入过程。如果磁盘I/O层或存储设备具有回写式缓存,则脏缓存数据必须立即提交到介质。如果对介质的每个写操作都在以下时间内完成,则此命令不执行任何操作 disk_write 功能。return RES_OK;case GET_SECTOR_COUNT:{*(DWORD *)buff = 256;     //表示扇区的个数return RES_OK;}		case GET_SECTOR_SIZE:{*(WORD *)buff = 2048;  //表示每个扇区的大小return RES_OK;}	case GET_BLOCK_SIZE:{*(WORD *)buff = 1;  //表示同时可擦除的扇区个数return RES_OK;}	default:res = RES_ERROR;}return res;/* USER CODE END IOCTL */
}

这里的读写函数需要加上地址偏移
每次操作2048个字节
扇区个数为256 对应Flash的256页
扇区大小即位页大小 2048字节
每次同时擦除1个扇区也就是1页

加入使用多页擦除的话 譬如2页擦除 则中间需要缓存的数据就为2048*2 这会大大占用系统资源 但能有效提高读写速度 不过在嵌入式系统中不建议这样做
另外配置堆栈大小 越大越好
在这里插入图片描述

时间戳函数

在文件fatfs.c中修改时间戳函数

/*** @brief  Gets Time from RTC* @param  None* @retval Time in DWORD*/
DWORD get_fattime(void)
{/* USER CODE BEGIN get_fattime */RTC_TimeTypeDef sTime;RTC_DateTypeDef sDate;//获取RTC时间if(HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK){//获取RTC日期HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);WORD date=(2000+sDate.Year-1980)<<9;date = date |(sDate.Month<<5) |sDate.Date;WORD time=sTime.Hours<<11;time = time | (sTime.Minutes<<5) | (sTime.Seconds>1);DWORD dt=(date<<16) | time;return	dt;}elsereturn 0;/* USER CODE END get_fattime */
}

文件操作函数

建立一个文件 加上文件操作等函数
头文件声明:

#ifndef FILE_OPERATE_H
#define FILE_OPERATE_H#include "main.h"
#include "FatFs.h"
#include "stdio.h"/*函数声明*/
void FatFS_Init(void);void FatFs_GetDiskInfo(void);
void FatFs_ScanDir(const TCHAR* PathName);
void FatFs_ReadTXTFile(TCHAR *filename);
void FatFs_WriteTXTFile(TCHAR *filename,uint16_t year, uint8_t month, uint8_t day);
void FatFs_GetFileInfo(TCHAR *filename);
void FatFs_DeleteFile(TCHAR *filename);
void FatFs_PrintfFileDate(WORD date, WORD time);#endif

工作区缓存

//定义用于格式化的工作区缓存
BYTE work[_MAX_SS];

由于一次只操作一个扇区 所以缓存大小即为2048

文件挂载和格式化测试

	retUSER = f_mount(&USERFatFS,USERPath,1);//挂载盘符Aif(retUSER == FR_NO_FILESYSTEM)//没有文件系统就格式化创建创建文件系统{retUSER = f_mkfs(USERPath,FM_FAT,2048,work,sizeof(work));if(retUSER == FR_OK){retUSER = f_mount(&USERFatFS,USERPath,1);//挂载printf("[FatFS] 格式化成功retUSER=%d\r\n",retUSER);}else{printf("[FatFS] 格式化失败retUSER=%d\r\n",retUSER);return;}//格式化失败}else if(retUSER == FR_OK){printf("[FatFS] 挂载成功retUSER=%d\r\n",retUSER);}else{printf("[FatFS] 挂载失败retUSER=%d\r\n",retUSER);return;}//挂载失败

第一次时首先挂载 若未成功则重新格式化再挂载

需要注意的是 格式化后 Flash内容尽量不要发生改变
若不慎改变 则很可能在挂载时会卡死 建议执行重新格式化
最好的方法就是把首个文件系统扇区进行擦除 然后让函数重新执行格式化

在格式化中 f_mkfs函数的传参除了路径、文件系统类型外 其工作区和工作区大小 以及分配单元大小都要与2048对齐

文件读写测试

若挂载成功 则可以进行文件读写测试

void SDFileTestWrite(void)
{FRESULT res_sd;UINT fnum;/* 文件成功读写数量 */char string[100];signed int ByteNum = 0;memset(string,0,sizeof(string));sprintf(string,"%s%s.xls",USERPath,"Test");res_sd = f_open(&USERFile, string,FA_CREATE_ALWAYS | FA_WRITE );if(res_sd != FR_OK){printf("[FILE] Failed to create file! %d\r\n",res_sd);}sprintf(string,"Vreal\tA1\tA2\n");ByteNum = strlen(string);res_sd=f_write(&USERFile,string,ByteNum,&fnum);res_sd = f_close(&USERFile);if(res_sd != FR_OK){printf("[FILE] Error:File closure Exception!\r\n");}else{printf("[FILE] SDFileTestWrite ok!\r\n");}
}void SDFileTestRead(void)
{FRESULT res_sd;char string[100];uint32_t line = 0;memset(string,0,sizeof(string));sprintf(string,"%s%s.xls",USERPath,"Test");res_sd = f_open(&USERFile, string, FA_OPEN_EXISTING | FA_READ);if(res_sd != FR_OK){goto LoadFail;}line = 0;while(!(f_eof(&USERFile))){memset(string,0,sizeof(string));f_gets(string,sizeof(string),&USERFile);if(strlen(string) == 0){break;}++line;printf("[FILE] line:%d %s\r\n",line,string);//sscanf(string,"%f\t%f\t%f\n",&Vreal[*pNum],&Va1[*pNum],&Va2[*pNum]);//按格式提取字符串函数}res_sd = f_close(&USERFile);if(res_sd != FR_OK){printf("[FILE] Error:Load File closure Exception!\r\n");}printf("[FILE] SDFileTestRead ok\r\n");return;LoadFail:{printf("[FILE] Load Fail:%s\r\n",string);}
}

此函数实现了创建一个xls文件 并读取

其他文件操作函数

包括但不限于 查看目录所有文件、添加/删除文件、文件信息浏览等等
其实就是C语言文件操作那一些函数罢了 对应在Linux中就是ls、mkdir、touch等等 具体的模拟CLI实现可以用串口来进行
完整代码:

#include "file_operate.h"
#include <string.h>//定义用于格式化的工作区缓存
BYTE work[_MAX_SS];void SDFileTestWrite(void)
{FRESULT res_sd;UINT fnum;/* 文件成功读写数量 */char string[100];signed int ByteNum = 0;memset(string,0,sizeof(string));sprintf(string,"%s%s.xls",USERPath,"Test");res_sd = f_open(&USERFile, string,FA_CREATE_ALWAYS | FA_WRITE );if(res_sd != FR_OK){printf("[FILE] Failed to create file! %d\r\n",res_sd);}sprintf(string,"Vreal\tA1\tA2\n");ByteNum = strlen(string);res_sd=f_write(&USERFile,string,ByteNum,&fnum);res_sd = f_close(&USERFile);if(res_sd != FR_OK){printf("[FILE] Error:File closure Exception!\r\n");}else{printf("[FILE] SDFileTestWrite ok!\r\n");}
}void SDFileTestRead(void)
{FRESULT res_sd;char string[100];uint32_t line = 0;memset(string,0,sizeof(string));sprintf(string,"%s%s.xls",USERPath,"Test");res_sd = f_open(&USERFile, string, FA_OPEN_EXISTING | FA_READ);if(res_sd != FR_OK){goto LoadFail;}line = 0;while(!(f_eof(&USERFile))){memset(string,0,sizeof(string));f_gets(string,sizeof(string),&USERFile);if(strlen(string) == 0){break;}++line;printf("[FILE] line:%d %s\r\n",line,string);//sscanf(string,"%f\t%f\t%f\n",&Vreal[*pNum],&Va1[*pNum],&Va2[*pNum]);//按格式提取字符串函数}res_sd = f_close(&USERFile);if(res_sd != FR_OK){printf("[FILE] Error:Load File closure Exception!\r\n");}printf("[FILE] SDFileTestRead ok\r\n");return;LoadFail:{printf("[FILE] Load Fail:%s\r\n",string);}
}
/*挂载FatFs文件系统*/
void FatFS_Init(void)
{	retUSER = f_mount(&USERFatFS,USERPath,1);//挂载盘符Aif(retUSER == FR_NO_FILESYSTEM)//没有文件系统就格式化创建创建文件系统{retUSER = f_mkfs(USERPath,FM_FAT,2048,work,sizeof(work));if(retUSER == FR_OK){retUSER = f_mount(&USERFatFS,USERPath,1);//挂载printf("[FatFS] 格式化成功retUSER=%d\r\n",retUSER);}else{printf("[FatFS] 格式化失败retUSER=%d\r\n",retUSER);return;}//格式化失败}else if(retUSER == FR_OK){printf("[FatFS] 挂载成功retUSER=%d\r\n",retUSER);}else{printf("[FatFS] 挂载失败retUSER=%d\r\n",retUSER);return;}//挂载失败SDFileTestWrite();SDFileTestRead();FatFs_GetDiskInfo();FatFs_ScanDir(USERPath);
}/*获取磁盘信息并在LCD上显示*/
void FatFs_GetDiskInfo(void)
{FATFS *fs;//定义剩余簇个数变量DWORD fre_clust; //获取剩余簇个数FRESULT res = f_getfree("0:", &fre_clust, &fs); //获取失败if(res != FR_OK){printf("f_getfree() error\r\n");return;}printf("\r\n*** FAT disk info ***\r\n");//总的扇区个数DWORD tot_sect = (fs->n_fatent - 2) * fs->csize;  //剩余的扇区个数 = 剩余簇个数 * 每个簇的扇区个数DWORD fre_sect = fre_clust * fs->csize;    //对于SD卡和U盘, _MIN_SS=512字节
#if  _MAX_SS == _MIN_SS  //SD卡的_MIN_SS固定为512,右移11位相当于除以2048//剩余空间大小,单位:MB,用于SD卡,U盘DWORD freespace= (fre_sect>>11); //总空间大小,单位:MB,用于SD卡,U盘		DWORD totalSpace= (tot_sect>>11);  
#else//Flash存储器,小容量//剩余空间大小,单位:KBDWORD freespace= (fre_sect*fs->ssize)>>10;   //总空间大小,单位:KBDWORD totalSpace= (tot_sect*fs->ssize)>>10;  
#endif//FAT类型printf("FAT type = %d\r\n",fs->fs_type);printf("[1=FAT12,2=FAT16,3=FAT32,4=exFAT]\r\n");//扇区大小,单位字节printf("Sector size(bytes) = ");//SD卡固定512字节
#if  _MAX_SS == _MIN_SS printf("%d\r\n", _MIN_SS);
#else//FLASH存储器printf("%d\r\n", fs->ssize);
#endifprintf("Cluster size(sectors) = %d\r\n", fs->csize);printf("Total cluster count = %ld\r\n", fs->n_fatent-2);printf("Total sector count = %ld\r\n", tot_sect);//总空间
#if  _MAX_SS == _MIN_SS printf("Total space(MB) = %ld\r\n", totalSpace);
#elseprintf("Total space(KB) = %ld\r\n", totalSpace);
#endif//空闲簇数量printf("Free cluster count = %ld\r\n",fre_clust);//空闲扇区数量printf("Free sector count = %ld\r\n", fre_sect);//空闲空间
#if  _MAX_SS == _MIN_SS printf("Free space(MB) = %ld\r\n", freespace);
#elseprintf("Free space(KB) = %ld\r\n", freespace);
#endifprintf("Get FAT disk info OK\r\n");
}/*创建文本文件*/
void FatFs_WriteTXTFile(TCHAR *filename,uint16_t year, uint8_t month, uint8_t day)
{FIL	file;printf("\r\n*** Creating TXT file: %s ***\r\n", filename);FRESULT res = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);//打开/创建文件成功if(res == FR_OK){//字符串必须有换行符"\n"TCHAR str[]="Line1: Hello, FatFs***\n";  //不会写入结束符"\0"f_puts(str, &file); printf("Write file OK: %s\r\n", filename);}else{printf("Open file error,error code: %d\r\n", res);}//使用完毕关闭文件f_close(&file);
}/*读取一个文本文件的内容*/
void FatFs_ReadTXTFile(TCHAR *filename)
{printf("\r\n*** Reading TXT file: %s ***\r\n", filename);FIL	file;//以只读方式打开文件FRESULT res = f_open(&file, filename, FA_READ);  //打开成功if(res == FR_OK){//读取缓存TCHAR str[100];//没有读到文件内容末尾while(!f_eof(&file)){//读取1个字符串,自动加上结束符”\0”f_gets(str,100, &file);	printf("%s", str);}printf("\r\n");}//如果没有该文件else if(res == FR_NO_FILE)printf("File does not exist\r\n");//打开失败elseprintf("f_open() error,error code: %d\r\n", res);//关闭文件f_close(&file);
}/*扫描和显示指定目录下的文件和目录*/
void FatFs_ScanDir(const TCHAR* PathName)
{DIR dir;					//目录对象FILINFO fno;				//文件信息//打开目录FRESULT res = f_opendir(&dir, PathName);//打开失败if(res != FR_OK){//关闭目录,直接退出函数f_closedir(&dir);printf("\r\nf_opendir() error,error code: %d\r\n", res);return;}printf("\r\n*** All entries in dir: %s ***\r\n", PathName);//顺序读取目录中的文件while(1){//读取目录下的一个项res = f_readdir(&dir, &fno);    //文件名为空表示没有多的项可读了if(res != FR_OK || fno.fname[0] == 0)break;  //如果是一个目录if(fno.fattrib & AM_DIR)  		{printf("DIR: %s\r\n", fno.fname);}//如果是一个文件else  		{printf("FILE: %s\r\n",fno.fname);}}//扫描完毕,关闭目录printf("Scan dir OK\r\n");f_closedir(&dir);
}/*获取一个文件的文件信息*/
void FatFs_GetFileInfo(TCHAR *filename)
{printf("\r\n*** File info of: %s ***\r\n", filename);FILINFO fno;//检查文件或子目录是否存在FRESULT fr = f_stat(filename, &fno);//如果存在从fno中读取文件信息if(fr == FR_OK){printf("File size(bytes) = %ld\r\n", fno.fsize);printf("File attribute = 0x%x\r\n", fno.fattrib);printf("File Name = %s\r\n", fno.fname);//输出创建/修改文件时的时间戳FatFs_PrintfFileDate(fno.fdate, fno.ftime);}//如果没有该文件else if (fr == FR_NO_FILE)printf("File does not exist\r\n");//发生其他错误elseprintf("f_stat() error,error code: %d\r\n", fr);
}/*删除文件*/
void FatFs_DeleteFile(TCHAR *filename)
{printf("\r\n*** Delete File: %s ***\r\n", filename);FIL	file;//打开文件FRESULT res = f_open(&file, filename, FA_OPEN_EXISTING);  if(res == FR_OK){//关闭文件f_close(&file);printf("open successfully!\r\n");}//删除文件res = f_unlink(filename);//删除成功if(res == FR_OK){printf("The file was deleted successfully!\r\n");}//删除失败else{printf("File deletion failed, error code:%d\r\n", res);}
}/*打印输出文件日期*/
void FatFs_PrintfFileDate(WORD date, WORD time)
{printf("File data = %d/%d/%d\r\n", ((date>>9)&0x7F)+1980, (date>>5)&0xF, date&0x1F);printf("File time = %d:%d:%d\r\n", (time>>11)&0x1F, (time>>5)&0x3F, time&0x1F);
}

MSC配置

开启USB_FS即可 这里选择Device_Only
在这里插入图片描述
NVIC中开启中断 其他不用改

如果使用HS(高速) 需要物理芯片
而FS则上拉电阻即可
具体看手册

在外设中配置MSC 并配置扇区大小(最好与Flash的最小读写单元保持一致)

在这里插入图片描述
这里是用的最大值2048
在这里插入图片描述

工程配置

添加如下文件
在这里插入图片描述
在这里插入图片描述
并添加USB内核和MSC的头文件路径:
在这里插入图片描述
在这里插入图片描述
最后编译就行了

然后修改usbd_storage_if.c文件
设备初始化:
在这里插入图片描述
读写锁(判断是否繁忙):
在这里插入图片描述
读写函数:
在这里插入图片描述

另外 头部定义修改为cubemx中一致

#define STORAGE_LUN_NBR                  1
#define STORAGE_BLK_NBR                  256
#define STORAGE_BLK_SIZ                  2048

然后连上PC就能看到U盘了
在这里插入图片描述
在这里插入图片描述

测试

在格式化前 数据都是FF
在这里插入图片描述
在挂载测试时 会读取整个硬盘数据 发现没数据 就会报挂载不成功 然后开始格式化

格式化时 写入的第一个地址内容如下:
在这里插入图片描述
格式化完成后:
在这里插入图片描述
在这里插入图片描述
这些都是底层操作 我们不用考虑 只要文件系统没BUG 就肯定能跑
格式化成功测试:
在这里插入图片描述
在测试之前 我跑了一下Flash Test 其会将0x0808 0000的整个页面清空
所以 每次复位都会重新格式化
去掉Flash Test后则能直接挂载:
在这里插入图片描述
硬盘信息:
在这里插入图片描述
目录下所有文件信息:
在这里插入图片描述

附录:Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作

SysTick系统定时器精准延时

延时函数

SysTick->LOAD中的值为计数值
计算方法为工作频率值/分频值
比如工作频率/1000 则周期为1ms

以ADuCM4050为例:

#include "ADuCM4050.h"void delay_ms(unsigned int ms)
{SysTick->LOAD = 26000000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能52MHz的系统定时器while(ms--){while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待}SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{SysTick->LOAD = 26000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能52MHz的系统定时器while(us--){while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待}SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

其中的52000000表示芯片的系统定时器频率 32系列一般为外部定时器频率的两倍

Cortex-M架构SysTick系统定时器阻塞和非阻塞延时

阻塞延时

首先是最常用的阻塞延时

void delay_ms(unsigned int ms)
{SysTick->LOAD = 50000000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器while(ms--){while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待}SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{SysTick->LOAD = 50000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器while(us--){while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待}SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

50000000表示工作频率
分频后即可得到不同的延时时间
以此类推

那么 不用两个嵌套while循环 也可以写成:

void delay_ms(unsigned int ms)
{SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

但是这种写法有个弊端
那就是输入ms后,最大定时不得超过计数值,也就是不能超过LOAD的最大值,否则溢出以后,则无法正常工作

而LOAD如果最大是32位 也就是4294967295

晶振为50M的话 50M的计数值为1s 4294967295计数值约为85s

固最大定时时间为85s

但用嵌套while的话 最大可以支持定时4294967295*85s

非阻塞延时

如果采用非阻塞的话 直接改写第二种方法就好了:

void delay_ms(unsigned int ms)
{SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待//SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待//SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

将等待和关闭定时器语句去掉
在使用时加上判断即可变为阻塞:

delay_ms(500);
while ((SysTick->CTRL & 0x00010000)==0);
SysTick->CTRL = 0;

在非阻塞状态下 可以提交定时器后 去做别的事情 然后再来等待

不过这样又有一个弊端 那就是定时器会自动重载 可能做别的事情以后 定时器跑过了 然后就要等85s才能停下

故可以通过内部定时器来进行非阻塞延时函数的编写

基本上每个mcu的内部定时器都可以配置自动重载等功能 网上资料很多 这里就不再阐述了

位带操作

位带代码

M3、M4架构的单片机 其输出口地址为端口地址+20 输入为+16
M0架构的单片机 其输出口地址为端口地址+12 输入为+8
以ADuCM4050为列:

位带宏定义
#ifndef __GPIO_H__
#define __GPIO_H__
#include "ADuCM4050.h"
#include "adi_gpio.h"#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))#define GPIO0_ODR_Addr    (ADI_GPIO0_BASE+20) //0x40020014
#define GPIO0_IDR_Addr    (ADI_GPIO0_BASE+16) //0x40020010#define GPIO1_ODR_Addr    (ADI_GPIO1_BASE+20) //0x40020054
#define GPIO1_IDR_Addr    (ADI_GPIO1_BASE+16) //0x40020050#define GPIO2_ODR_Addr    (ADI_GPIO2_BASE+20) //0x40020094
#define GPIO2_IDR_Addr    (ADI_GPIO2_BASE+16) //0x40020090#define GPIO3_ODR_Addr    (ADI_GPIO3_BASE+20) //0x400200D4
#define GPIO3_IDR_Addr    (ADI_GPIO3_BASE+16) //0x400200D0#define P0_O(n)   	BIT_ADDR(GPIO0_ODR_Addr,n)  //输出 
#define P0_I(n)    	BIT_ADDR(GPIO0_IDR_Addr,n)  //输入 #define P1_O(n)   	BIT_ADDR(GPIO1_ODR_Addr,n)  //输出 
#define P1_I(n)    	BIT_ADDR(GPIO1_IDR_Addr,n)  //输入 #define P2_O(n)   	BIT_ADDR(GPIO2_ODR_Addr,n)  //输出 
#define P2_I(n)    	BIT_ADDR(GPIO2_IDR_Addr,n)  //输入 #define P3_O(n)   	BIT_ADDR(GPIO3_ODR_Addr,n)  //输出 
#define P3_I(n)    	BIT_ADDR(GPIO3_IDR_Addr,n)  //输入 #define Port0			(ADI_GPIO_PORT0)
#define Port1			(ADI_GPIO_PORT1)
#define Port2			(ADI_GPIO_PORT2)
#define Port3			(ADI_GPIO_PORT3)#define Pin0			(ADI_GPIO_PIN_0)
#define Pin1			(ADI_GPIO_PIN_1)
#define Pin2			(ADI_GPIO_PIN_2)
#define Pin3			(ADI_GPIO_PIN_3)
#define Pin4			(ADI_GPIO_PIN_4)
#define Pin5			(ADI_GPIO_PIN_5)
#define Pin6			(ADI_GPIO_PIN_6)
#define Pin7			(ADI_GPIO_PIN_7)
#define Pin8			(ADI_GPIO_PIN_8)
#define Pin9			(ADI_GPIO_PIN_9)
#define Pin10			(ADI_GPIO_PIN_10)
#define Pin11			(ADI_GPIO_PIN_11)
#define Pin12			(ADI_GPIO_PIN_12)
#define Pin13			(ADI_GPIO_PIN_13)
#define Pin14			(ADI_GPIO_PIN_14)
#define Pin15			(ADI_GPIO_PIN_15)void GPIO_OUT(unsigned int port,unsigned int pin,unsigned int flag);
void GPIO_BUS_OUT(unsigned int port,unsigned int num);void P0_BUS_O(unsigned int num);
unsigned int P0_BUS_I(void);void P1_BUS_O(unsigned int num);
unsigned int P1_BUS_I(void);void P2_BUS_O(unsigned int num);
unsigned int P2_BUS_I(void);void P3_BUS_O(unsigned int num);
unsigned int P3_BUS_I(void);#endif
总线函数
#include "ADuCM4050.h"
#include "adi_gpio.h"
#include "GPIO.h"void GPIO_OUT(unsigned int port,unsigned int pin,unsigned int flag)
{switch(port){case 0:{switch(pin){case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_0));};break;case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_1));};break;case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_2));};break;case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_3));};break;case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_4));};break;case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_5));};break;case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_6));};break;case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_7));};break;case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_8));};break;case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_9));};break;case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_10));};break;case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_11));};break;case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_12));};break;case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_13));};break;case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_14));};break;case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_15));};break;default:pin=0;break;}}break;case 1:{switch(pin){case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_0));};break;case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_1));};break;case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_2));};break;case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_3));};break;case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_4));};break;case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_5));};break;case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_6));};break;case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_7));};break;case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_8));};break;case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_9));};break;case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_10));};break;case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_11));};break;case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_12));};break;case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_13));};break;case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_14));};break;case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_15));};break;default:pin=0;break;}}break;case 2:{switch(pin){case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_0));};break;case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_1));};break;case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_2));};break;case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_3));};break;case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_4));};break;case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_5));};break;case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_6));};break;case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_7));};break;case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_8));};break;case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_9));};break;case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_10));};break;case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_11));};break;case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_12));};break;case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_13));};break;case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_14));};break;case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_15));};break;default:pin=0;break;}}break;case 3:{switch(pin){case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_0));};break;case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_1));};break;case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_2));};break;case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_3));};break;case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_4));};break;case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_5));};break;case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_6));};break;case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_7));};break;case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_8));};break;case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_9));};break;case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_10));};break;case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_11));};break;case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_12));};break;case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_13));};break;case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_14));};break;case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_15));};break;default:pin=0;break;}}break;default:port=0;break;}	
}void GPIO_BUS_OUT(unsigned int port,unsigned int num)  //num最大为0xffff
{int i;for(i=0;i<16;i++){GPIO_OUT(port,i,(num>>i)&0x0001);}
}void P0_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){P0_O(i)=(num>>i)&0x0001;}
}
unsigned int P0_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(P0_I(i)<<i)&0xFFFF;}return num;
}void P1_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){P1_O(i)=(num>>i)&0x0001;}
}
unsigned int P1_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(P1_I(i)<<i)&0xFFFF;}return num;
}void P2_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){P2_O(i)=(num>>i)&0x0001;}
}
unsigned int P2_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(P2_I(i)<<i)&0xFFFF;}return num;
}void P3_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){P3_O(i)=(num>>i)&0x0001;}
}
unsigned int P3_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(P3_I(i)<<i)&0xFFFF;}return num;
}

一、位带操作理论及实践

位带操作的概念其实30年前就有了,那还是 CM3 将此能力进化,这里的位带操作是 8051 位寻址区的威力大幅加强版

位带区: 支持位带操作的地址区

位带别名: 对别名地址的访问最终作 用到位带区的访问上(注意:这中途有一个 地址映射过程)

位带操作对于硬件 I/O 密集型的底层程序最有用处

支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在CM4中,有两个区中实现了位带。其中一个是SRAM区的最低1MB范围,第二个则是片内外设区的最低1MB范围。这两个区中的地址除了可以像普通的RAM一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个32位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。

位操作就是可以单独的对一个比特位读和写,类似与51中sbit定义的变量,stm32中通过访问位带别名区来实现位操作的功能
STM32中有两个地方实现了位带,一个是SRAM,一个是片上外设。
在这里插入图片描述
(1)位带本质上是一块地址区(例如每一位地址位对应一个寄存器)映射到另一片地址区(实现每一位地址位对应一个寄存器中的一位),该区域就叫做位带别名区,将每一位膨胀成一个32位的字。
(2)位带区的4个字节对应实际寄存器或内存区的一个位,虽然变大到4个字节,但实际上只有最低位有效(代表0或1)

只有位带可以直接用=赋值的方式来操作寄存器 位带是把寄存器上的每一位 膨胀到32位 映射到位带区 比如0x4002 0000地址的第0个bit 映射到位带区的0地址 那么其对应的位带映射地址为0x00 - 0x04 一共32位 但只有LSB有效 采用位带的方式用=赋值时 就是把位带区对应的LSB赋值 然后MCU再转到寄存器对应的位里面 寄存器操作时 如果不改变其他位上面的值 那就只能通过&=或者|=的方式进行

在这里插入图片描述

要设置0x2000 0000这个字节的第二个位bit2为1,使用位带操作的步骤有:
1、将1写入位 带别名区对应的映射地址(即0x22000008,因为1bit对应4个byte);
2、将0x2000 0000的值 读取到内部的缓冲区(这一步骤是内核完成的,属于原子操作,不需要用户操作);
3、将bit2置1,再把值写 回到0x2000 0000(属于原子操作,不需要用户操作)。

关于GPIO引脚对应的访问地址,可以参考以下公式
寄存器位带别名 = 0x42000000 + (寄存器的地址-0x40000000)32 + 引脚编号4

如:端口F访问的起始地址GPIOF_BASE

#define GPIOF ((GPIO_TypeDef *)GPIOF_BASE)

在这里插入图片描述

但好在官方库里面都帮我们定义好了 只需要在BASE地址加上便宜即可

例如:

GPIOF的ODR寄存器的地址 = GPIOF_BASE + 0x14

寄存器位带别名 = 0x42000000 + (寄存器的地址-0x40000000)32 + 引脚编号4

设置PF9引脚的话:

uint32_t *PF9_BitBand =
*(uint32_t *)(0x42000000 + ((uint32_t )&GPIOF->ODR– 0x40000000) *32 + 9*4)

封装一下:

#define PFout(x) *(volatile uint32_t *)(0x42000000 + ((uint32_t )&GPIOF->ODR – 0x40000000) *32 + x*4)

现在 可以把通用部分封装成一个小定义:

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

那么 设置PF引脚的函数可以定义:

#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414   
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 #define PF_O(n)   	BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PF_I(n)    	BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

若使PF9输入输出则:

PF_O(9)=1;  //输出高电平
uint8_t dat = PF_I(9);  //获取PF9引脚的值

总线输入输出:

void PF_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){PF_O(i)=(num>>i)&0x0001;}
}
unsigned int PF_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(PF_I(i)<<i)&0xFFFF;}return num;
}

STM32的可用下面的函数:

#ifndef __GPIO_H__
#define __GPIO_H__
#include "stm32l496xx.h"#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     #define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 #define PA_O(n)   	BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PA_I(n)    	BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 #define PB_O(n)   	BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PB_I(n)    	BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 #define PC_O(n)   	BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PC_I(n)    	BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 #define PD_O(n)   	BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PD_I(n)    	BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 #define PE_O(n)   	BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PE_I(n)    	BIT_ADDR(GPIOE_IDR_Addr,n)  //输入#define PF_O(n)   	BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PF_I(n)    	BIT_ADDR(GPIOF_IDR_Addr,n)  //输入#define PG_O(n)   	BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PG_I(n)    	BIT_ADDR(GPIOG_IDR_Addr,n)  //输入#define PH_O(n)   	BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PH_I(n)    	BIT_ADDR(GPIOH_IDR_Addr,n)  //输入#define PI_O(n)			BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PI_I(n)   	BIT_ADDR(GPIOI_IDR_Addr,n)  //输入void PA_BUS_O(unsigned int num);
unsigned int PA_BUS_I(void);void PB_BUS_O(unsigned int num);
unsigned int PB_BUS_I(void);void PC_BUS_O(unsigned int num);
unsigned int PC_BUS_I(void);void PD_BUS_O(unsigned int num);
unsigned int PD_BUS_I(void);void PE_BUS_O(unsigned int num);
unsigned int PE_BUS_I(void);void PF_BUS_O(unsigned int num);
unsigned int PF_BUS_I(void);void PG_BUS_O(unsigned int num);
unsigned int PG_BUS_I(void);void PH_BUS_O(unsigned int num);
unsigned int PH_BUS_I(void);void PI_BUS_O(unsigned int num);
unsigned int PI_BUS_I(void);#endif
#include "GPIO.h"void PA_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){PA_O(i)=(num>>i)&0x0001;}
}
unsigned int PA_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(PA_I(i)<<i)&0xFFFF;}return num;
}void PB_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){PB_O(i)=(num>>i)&0x0001;}
}
unsigned int PB_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(PB_I(i)<<i)&0xFFFF;}return num;
}void PC_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){PC_O(i)=(num>>i)&0x0001;}
}
unsigned int PC_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(PC_I(i)<<i)&0xFFFF;}return num;
}void PD_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){PD_O(i)=(num>>i)&0x0001;}
}
unsigned int PD_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(PD_I(i)<<i)&0xFFFF;}return num;
}void PE_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){PE_O(i)=(num>>i)&0x0001;}
}
unsigned int PE_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(PE_I(i)<<i)&0xFFFF;}return num;
}void PF_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){PF_O(i)=(num>>i)&0x0001;}
}
unsigned int PF_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(PF_I(i)<<i)&0xFFFF;}return num;
}void PG_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){PG_O(i)=(num>>i)&0x0001;}
}
unsigned int PG_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(PG_I(i)<<i)&0xFFFF;}return num;
}void PH_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){PH_O(i)=(num>>i)&0x0001;}
}
unsigned int PH_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(PH_I(i)<<i)&0xFFFF;}return num;
}void PI_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
{int i;for(i=0;i<16;i++){PI_O(i)=(num>>i)&0x0001;}
}
unsigned int PI_BUS_I(void)  //输出值num最大为0xFFFF
{unsigned int num;int i;for(i=0;i<16;i++){num=num+(PI_I(i)<<i)&0xFFFF;}return num;
}

二、如何判断MCU的外设是否支持位带

根据《ARM Cortex-M3与Cortex-M4权威指南(第3版)》中第6章第7节描述
在这里插入图片描述
也就是说 要实现对GPIO的位带操作 必须保证GPIO位于外设区域的第一个1MB中
第一个1MB应该是0x4010 0000之前 位带不是直接操作地址 而是操作地址映射 地址映射被操作以后 MCU自动会修改对应寄存器的值

位带区只有1MB 所以只能改0x4000 0000 - 0x400F FFFF的寄存器
像F4系列 GPIO的首地址为0x4002 0000 就可以用位带来更改

STM32L476的GPIO就不行:
在这里插入图片描述
AHB2的都不能用位带
ABP 还有AHB1都可以用
在这里插入图片描述
但是L476的寄存器里面 GPIO和ADC都是AHB2

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

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

相关文章

打穿内网三重奏-红日7

靶机下载地址&#xff1a; 漏洞详情 (qiyuanxuetang.net) 攻击链路&#xff1a; DMZ区IP段为192.168.11.1/24 第二层网络环境IP段为192.168.52.1/24 第三层网络环境IP段为192.168.93.1/24 这里DMZ和攻击者我用的是192.168.11.1 这个网段&#xff0c;其他不变 这里我加了两张…

Vue.js 在低代码开发平台中的应用与优化

Vue.js 在低代码开发平台中的应用与优化 在数字化转型的进程中&#xff0c;低代码开发平台成为了企业快速构建应用的得力助手。而 Vue.js 作为一款广受欢迎的前端框架&#xff0c;在低代码开发平台中发挥着举足轻重的作用。它不仅提升了开发效率&#xff0c;还优化了应用的用户…

QML 快捷键与Shortcut的使用

一、效果展示 二、源码分享 import QtQuick import QtQuick.Controls import Qt.labs.qmlmodels import QtQuick.Controls.Basic import QtQuick.Layouts import QtQuick.Effects import Qt.labs.platformApplicationWindow {id:rootwidth: 1000height: 730visible: truetitle…

RocketMQ和Kafka如何实现顺序写入和顺序消费?

0 前言 先说明kafka&#xff0c;顺序写入和消费是Kafka的重要特性&#xff0c;但需要正确的配置和使用方式才能保证。本文需要解释清楚Kafka如何通过分区来实现顺序性&#xff0c;以及生产者和消费者应该如何配合。   首先&#xff0c;顺序写入。Kafka的消息是按分区追加写入…

【南方Cass】快捷键0002:合并多段线

快捷键&#xff1a;JOIN 按下快捷键JOIN&#xff0c;然后选择需要合并的对象&#xff08;多段线&#xff09;&#xff0c;按下回车即可完成合并。

Qt的isVisible ()函数介绍和判断窗口是否在当前界面显示

1、现象&#xff1a;当Qt的窗口最小化时&#xff0c;isVisible值一定是true&#xff0c;这是正常的。 解释&#xff1a;在Qt中&#xff0c;当你点击窗口的最小化按钮时&#xff0c;Qt内部不会自动调用 hide() 方或 setVisible(false) 来隐藏窗口。相反&#xff0c;它会改变窗口…

【愚公系列】《Python网络爬虫从入门到精通》007-请求模块requests高级应用(Reguests-HTML)

标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…

【AI论文】CodeI/O: 通过代码输入输出预测来提炼推理模式

摘要&#xff1a;推理是大型语言模型的一项基本能力。尽管先前的研究主要集中在提升如数学或代码生成等狭窄领域的技能&#xff0c;但由于训练数据稀疏且分散&#xff0c;在许多其他推理任务上提高性能仍然具有挑战性。为了解决这个问题&#xff0c;我们提出了CodeI/O&#xff…

AI编程01-生成前/后端接口对表-豆包(或Deepseek+WPS的AI

前言: 做过全栈的工程师知道,如果一个APP的项目分别是前端/后端两个团队开发的话,那么原型设计之后,通过接口文档进行开发对接是非常必要的。 传统的方法是,大家一起定义一个接口文档,然后,前端和后端的工程师进行为何,现在AI的时代,是不是通过AI能协助呢,显然可以…

24电子信息类研究生复试面试问题汇总 电子信息类专业知识问题最全!电子信息复试全流程攻略 电子信息考研复试真题汇总

你是不是在为电子信息考研复试焦虑&#xff1f;害怕被老师问到刁钻问题、担心专业面答不上来&#xff1f;别慌&#xff01;作为复试面试92分逆袭上岸的学姐&#xff0c;今天手把手教你拆解电子信息类复试通关密码&#xff01;看完这篇&#xff0c;让你面试现场直接开大&#xf…

PortSwigger——WebSockets vulnerabilities

文章目录 一、WebSockets二、Lab: Manipulating WebSocket messages to exploit vulnerabilities三、Lab: Manipulating the WebSocket handshake to exploit vulnerabilities四、Using cross-site WebSockets to exploit vulnerabilities4.1 跨站WebSocket劫持&#xff08;cro…

Sonic Layer1

礼记有言&#xff1a;良冶之子&#xff0c;必学为裘&#xff1b;良弓之子&#xff0c;必学为箕&#xff1b; 闲来无趣&#xff0c;看看Sonic 的官方文档吧。道听途殊终归了解的不够全面。 首先&#xff0c;看Sonic 是如何介绍自己的&#xff1a; 哇趣&#xff0c;Sonic 把自己的…

html css js网页制作成品——HTML+CSS+js茉酸奶的茶网页设计(5页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…

在vscode中拉取gitee里的项目并运行

拉取项目: 方法一:vscode点击查看--->终端(或者直接通过快捷键ctrol+ `打开) 在终端内通过cd命令定位到你想存放项目的文件夹 例如:cd h: 通过命令:git clone 地址 例如:git clone newbee-mall-vue-app: 前端代码 等待拉取完成即可在对应文件夹下看到项目啦 方…

cap2:1000分类的ResNet的TensorRT部署指南(python版)

文章目录 1、保存pytorch模型1.1 获取pth模型1.2 建立标杆 2、导出ONNX2.1 导出模型2.2 验证模型2.3 可视化模型结构 3、环境搭建3.1 TensorRT的安装3.2 安装pycuda 4、转换TensorRT引擎4.1 使用trtexec工具完成序列化4.2 使用python的API进行转换 5、推理5.1 推理代码5.2 结果…

Mybatis快速入门与核心知识总结

Mybatis 1. 实体类&#xff08;Entity Class&#xff09;1.1 实体类的定义1.2 简化编写1.2.1 Data1.2.2 AllArgsConstructor1.2.3 NoArgsConstructor 2. 创建 Mapper 接口2.1 Param2.2 #{} 占位符2.3 SQL 预编译 3. 配置 MyBatis XML 映射文件&#xff08;可选&#xff09;3.1 …

python-leetcode 28.两数相加

题目&#xff1a; 给定两个非空链表&#xff0c;表示两个非负整数&#xff0c;它们每位数字都是按照逆序地方式存储&#xff0c;并且每个节点只能存储一位数字。请将两数相加&#xff0c;并以相同的形式返回一个表示和的链表。 输入&#xff1a;l1 [2,4,3], l2 [5,6,4] 输出…

【云安全】云原生- K8S API Server 未授权访问

API Server 是 Kubernetes 集群的核心管理接口&#xff0c;所有资源请求和操作都通过 kube-apiserver 提供的 API 进行处理。默认情况下&#xff0c;API Server 会监听两个端口&#xff1a;8080 和 6443。如果配置不当&#xff0c;可能会导致未授权访问的安全风险。 8080 端口…

微信小程序配置3 配置sass

1. 在config。json文件里面的setting配置“sass” 2. 改你需要的页面后缀名为scss。 3.查看页面即可看到样式。

16.React学习笔记.React更新机制

一. 发生更新的时机以及顺序## image.png props/state改变render函数重新执行产生新的VDOM树新旧DOM树进行diff计算出差异进行更新更新到真实的DOM 二. React更新流程## React将最好的O(n^3)的tree比较算法优化为O(n)。 同层节点之间相互比较&#xff0c;不跨节点。不同类型的节…