【STM32】通过L496的HAL库Flash创建FatFS文件体系(CubeMX主动设置R0.12C版 ...

打印 上一主题 下一主题

主题 1034|帖子 1034|积分 3102

【STM32】通过L496的HAL库Flash创建FatFS文件体系(CubeMX主动设置R0.12C版本)

  
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 或两者都擦除)
  1. /** @defgroup FLASH_Banks FLASH Banks
  2.   * @{
  3.   */
  4. #define FLASH_BANK_1              ((uint32_t)0x01)                          /*!< Bank 1   */
  5. #if defined (STM32L471xx) || defined (STM32L475xx) || defined (STM32L476xx) || defined (STM32L485xx) || defined (STM32L486xx) || \
  6.     defined (STM32L496xx) || defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || defined (STM32L4R5xx) || \
  7.     defined (STM32L4R7xx) || defined (STM32L4R9xx) || defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
  8. #define FLASH_BANK_2              ((uint32_t)0x02)                          /*!< Bank 2   */
  9. #define FLASH_BANK_BOTH           ((uint32_t)(FLASH_BANK_1 | FLASH_BANK_2)) /*!< Bank1 and Bank2  */
  10. #else
  11. #define FLASH_BANK_BOTH           ((uint32_t)(FLASH_BANK_1))                /*!< Bank 1   */
  12. #endif
复制代码
HAL库测试代码如下:
  1. void Test_Flash(uint32_t add)
  2. {
  3.         uint32_t error = 0;
  4.         uint64_t dat = 0x0123456776543210;//要写入的数据,必须得是双字64bit
  5.         uint64_t read_dat = 0 ;
  6.         FLASH_EraseInitTypeDef flash_dat;          //定义一个结构体变量,里面有擦除操作需要定义的变量
  7.        
  8.         HAL_FLASH_Unlock();                                    //第二步:解锁                        
  9.         flash_dat.TypeErase = FLASH_TYPEERASE_PAGES;         //擦除类型是“Page Erase” 仅删除页面 另外一个参数是全部删除
  10.         flash_dat.Page = (uint32_t)((add-0x08000000)/2048);            //擦除地址对应的页
  11.         flash_dat.NbPages = 1;                               //一次性擦除1页,可以是任意页
  12.         if(flash_dat.Page>255)
  13.         {
  14.                 flash_dat.Banks=2;
  15.         }
  16.         else
  17.         {
  18.                 flash_dat.Banks=1;
  19.         }
  20.         HAL_FLASHEx_Erase(&flash_dat,&error);            //第三步:参数写好后调用擦除函数
  21.         FLASH_WaitForLastOperation(0xFFFF);
  22.         HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, add, dat);//第四步:写入数据
  23.         HAL_FLASH_Lock();                                      //第五步:上锁
  24.        
  25.         read_dat = *(__I uint64_t *)add;           //读出flash中的数据
  26.         uint32_t read_dat1=read_dat>>32;
  27.         uint32_t read_dat2=read_dat&0x00000000FFFFFFFF;
  28.         printf("[INFO] Flash_Test:0x%08x 0x%08x\n",read_dat1,read_dat2);
  29. }
复制代码
步调就是:
解锁;
擦除;
写数据;
上锁。
若要在写入某个地址下的一部分数据时 需要擦除整个页面 然后再举行写入
所以假如要保留该页面下的其他数据 就应该在写入之前读取该页面数据 然后将某一部分修改的数据替换掉
之后再按页面整个写入
好在文件体系中 只要设置恰当 可以帮我们实现按页擦除、写入的功能
如许我们就只需要定义好地址写、地址读函数即可
这里需要注意 由于L496的Flash是按64位对其 而我们的MCU是32位 所以不建议直接举行64位移位操作
最好是用两个32位变量 来拼接成一个64位
并且需要注意的是 32位变量左移位时 不得操作32位 最好是先赋值给64位变量 再单独对64位变量举行操作
同理 在读取函数中 64位变量也建议拆分成两个32位变量举行读取操作
Flash地址写

  1. //读取SPI FLASH  
  2. //在指定地址开始读取指定长度的数据
  3. //pBuffer:数据存储区
  4. //ReadAddr:开始读取的地址(24bit)
  5. //NumByteToWrite:要读取的字节数(最大65535)
  6. void Write_Flash(const uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
  7. {
  8.         if(Flag_Flash_Busy==1)return;
  9.         Flag_Flash_Busy=1;
  10.         uint32_t Current_ADD = ReadAddr;       
  11.         uint32_t add =0;
  12.         uint32_t page=(uint32_t)((Current_ADD-0x08000000)/2048);
  13.         uint32_t first_add = Current_ADD;       
  14.         uint32_t judg_add = (page)*0x800+0x08000000+Flash_Page_Size;
  15.         uint32_t error = 0;
  16.         uint64_t dat = 0;                                                                                                                //要写入的数据,必须得是双字64bit
  17.         uint32_t dat_0=0;
  18.         uint32_t dat_1=0;
  19.         uint16_t i =0;
  20.         uint16_t j = NumByteToRead/8;
  21.         FLASH_EraseInitTypeDef flash_dat;          //定义一个结构体变量,里面有擦除操作需要定义的变量
  22.        
  23.         HAL_FLASH_Unlock();                                    //第二步:解锁                        
  24.         flash_dat.TypeErase = FLASH_TYPEERASE_PAGES;         //擦除类型是“Page Erase” 仅删除页面 另外一个参数是全部删除
  25.         flash_dat.Page = (uint32_t)((Current_ADD-0x08000000)/2048);            //擦除地址对应的页
  26.         flash_dat.NbPages = 1;                               //一次性擦除1页,可以是任意页
  27.         if(flash_dat.Page>255)
  28.         {
  29.                 flash_dat.Banks=2;
  30.         }
  31.         else
  32.         {
  33.                 flash_dat.Banks=1;
  34.         }
  35.         HAL_FLASHEx_Erase(&flash_dat,&error);            //第三步:参数写好后调用擦除函数
  36.         FLASH_WaitForLastOperation(0xFFFF);
  37.         for(i=0;i<j;i++)
  38.         {
  39.                 add = Current_ADD+i*8;
  40.                 if(add>=judg_add)
  41.                 {
  42.                         HAL_FLASH_Lock();     //第五步:上锁
  43.                         Flag_Flash_Busy=0;
  44.                         Write_Flash(pBuffer+i*8,add-first_add,NumByteToRead-i*8);
  45.                         return;
  46.                 }
  47.                 dat_0 = pBuffer[i*8+0]|(pBuffer[i*8+1]<<8)|(pBuffer[i*8+2]<<16)|(pBuffer[i*8+3]<<24);               
  48.                 dat_1 = pBuffer[i*8+4]|(pBuffer[i*8+5]<<8)|(pBuffer[i*8+6]<<16)|(pBuffer[i*8+7]<<24);
  49.                 dat = dat_1;
  50.                 dat = (dat<<32)|dat_0;
  51.                 HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, add, dat);  //第四步:写入数据
  52.         }
  53.        
  54.         HAL_FLASH_Lock();     //第五步:上锁
  55.         Flag_Flash_Busy=0;
  56. }
复制代码
Flash地址读

  1. //读取SPI FLASH  
  2. //在指定地址开始读取指定长度的数据
  3. //pBuffer:数据存储区
  4. //ReadAddr:开始读取的地址(24bit)
  5. //NumByteToRead:要读取的字节数(最大65535)
  6. void Read_Flash(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
  7. {
  8.         if(Flag_Flash_Busy==1)return;
  9.         Flag_Flash_Busy=1;
  10.         uint32_t Current_ADD = ReadAddr;       
  11.         uint32_t add =0;
  12.         uint64_t dat = 0;                                                                                                                //要写入的数据,必须得是双字64bit
  13.         uint32_t dat_0=0;
  14.         uint32_t dat_1=0;
  15.         uint16_t i =0;
  16.         uint16_t j = NumByteToRead/8;
  17.        
  18.         for(i=0;i<j;i++)
  19.         {
  20.                 add = Current_ADD+i*8;
  21.                 dat = *(__I uint64_t *)(add);
  22.                 dat_1=dat>>32;
  23.                 dat_0=dat&0x00000000FFFFFFFF;
  24.                 pBuffer[i*8+0]=(uint8_t)(dat_0&0xFF);
  25.                 pBuffer[i*8+1]=(uint8_t)((dat_0>>8)&0xFF);
  26.                 pBuffer[i*8+2]=(uint8_t)((dat_0>>16)&0xFF);
  27.                 pBuffer[i*8+3]=(uint8_t)((dat_0>>24)&0xFF);
  28.                 pBuffer[i*8+4]=(uint8_t)(dat_1&0xFF);
  29.                 pBuffer[i*8+5]=(uint8_t)((dat_1>>8)&0xFF);
  30.                 pBuffer[i*8+6]=(uint8_t)((dat_1>>16)&0xFF);
  31.                 pBuffer[i*8+7]=(uint8_t)((dat_1>>24)&0xFF);
  32.         }
  33.         Flag_Flash_Busy=0;
  34. }
复制代码
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中的函数:
  1. /**
  2.   * @brief  Initializes a Drive
  3.   * @param  pdrv: Physical drive number (0..)
  4.   * @retval DSTATUS: Operation status
  5.   */
  6. DSTATUS USER_initialize (
  7.         BYTE pdrv           /* Physical drive nmuber to identify the drive */
  8. )
  9. {
  10.   /* USER CODE BEGIN INIT */
  11.     Stat = STA_NOINIT;
  12.                 //获取驱动器状态
  13.                 Stat = USER_status(pdrv);  
  14.     return Stat;
  15.   /* USER CODE END INIT */
  16. }
  17. /**
  18.   * @brief  Gets Disk Status
  19.   * @param  pdrv: Physical drive number (0..)
  20.   * @retval DSTATUS: Operation status
  21.   */
  22. DSTATUS USER_status (
  23.         BYTE pdrv       /* Physical drive number to identify the drive */
  24. )
  25. {
  26.   /* USER CODE BEGIN STATUS */
  27.                 Stat = STA_NOINIT;                  //驱动器未初始化,Stat=0x01
  28.                 Stat = 0 ;  //Stat=0x00
  29.     return Stat;
  30.   /* USER CODE END STATUS */
  31. }
  32. /**
  33.   * @brief  Reads Sector(s)
  34.   * @param  pdrv: Physical drive number (0..)
  35.   * @param  *buff: Data buffer to store read data
  36.   * @param  sector: Sector address (LBA)
  37.   * @param  count: Number of sectors to read (1..128)
  38.   * @retval DRESULT: Operation result
  39.   */
  40. DRESULT USER_read (
  41.         BYTE pdrv,      /* Physical drive nmuber to identify the drive */
  42.         BYTE *buff,     /* Data buffer to store read data */
  43.         DWORD sector,   /* Sector address in LBA */
  44.         UINT count      /* Number of sectors to read */
  45. )
  46. {
  47.   /* USER CODE BEGIN READ */
  48.         uint32_t globalAddr = (sector)*0x800+0x08080000;  
  49.         uint16_t byteCount = count << 11;   
  50.         //读取数据
  51.         Read_Flash((uint8_t *)buff,globalAddr, byteCount);
  52.     return RES_OK;
  53.   /* USER CODE END READ */
  54. }
  55. /**
  56.   * @brief  Writes Sector(s)
  57.   * @param  pdrv: Physical drive number (0..)
  58.   * @param  *buff: Data to be written
  59.   * @param  sector: Sector address (LBA)
  60.   * @param  count: Number of sectors to write (1..128)
  61.   * @retval DRESULT: Operation result
  62.   */
  63. #if _USE_WRITE == 1
  64. DRESULT USER_write (
  65.         BYTE pdrv,          /* Physical drive nmuber to identify the drive */
  66.         const BYTE *buff,   /* Data to be written */
  67.         DWORD sector,       /* Sector address in LBA */
  68.         UINT count          /* Number of sectors to write */
  69. )
  70. {
  71.   /* USER CODE BEGIN WRITE */
  72.   /* USER CODE HERE */
  73.         uint32_t globalAddr = (sector)*0x800+0x08080000;  
  74.         uint16_t byteCount = count << 11;   
  75.         Write_Flash((uint8_t *)buff,globalAddr, byteCount);
  76.     return RES_OK;
  77.   /* USER CODE END WRITE */
  78. }
  79. #endif /* _USE_WRITE == 1 */
  80. /**
  81.   * @brief  I/O control operation
  82.   * @param  pdrv: Physical drive number (0..)
  83.   * @param  cmd: Control code
  84.   * @param  *buff: Buffer to send/receive control data
  85.   * @retval DRESULT: Operation result
  86.   */
  87. #if _USE_IOCTL == 1
  88. DRESULT USER_ioctl (
  89.         BYTE pdrv,      /* Physical drive nmuber (0..) */
  90.         BYTE cmd,       /* Control code */
  91.         void *buff      /* Buffer to send/receive control data */
  92. )
  93. {
  94.   /* USER CODE BEGIN IOCTL */
  95.                 DRESULT res = RES_OK;
  96.        
  97.         switch(cmd)
  98.         {
  99.                 /*以下四个命令都是按照FatFs默认参数配置时必须需要的*/
  100.                 //完成挂起的写入过程(在_FS_READONLY == 0时需要)
  101.                 case CTRL_SYNC:               //确保设备已完成挂起的写入过程。如果磁盘I/O层或存储设备具有回写式缓存,则脏缓存数据必须立即提交到介质。如果对介质的每个写操作都在以下时间内完成,则此命令不执行任何操作 disk_write 功能。
  102.                                 return RES_OK;
  103.                         case GET_SECTOR_COUNT:{
  104.                                 *(DWORD *)buff = 256;     //表示扇区的个数
  105.                                 return RES_OK;
  106.                         }               
  107.                         case GET_SECTOR_SIZE:{
  108.                                 *(WORD *)buff = 2048;  //表示每个扇区的大小
  109.                                 return RES_OK;
  110.                         }       
  111.                                 case GET_BLOCK_SIZE:{
  112.                                 *(WORD *)buff = 1;  //表示同时可擦除的扇区个数
  113.                                 return RES_OK;
  114.                         }       
  115.                 default:
  116.                         res = RES_ERROR;
  117.         }
  118.     return res;
  119.   /* USER CODE END IOCTL */
  120. }
复制代码
这里的读写函数需要加上地址偏移
每次操作2048个字节
扇区个数为256 对应Flash的256页
扇区巨细即位页巨细 2048字节
每次同时擦除1个扇区也就是1页
加入利用多页擦除的话 譬如2页擦除 则中间需要缓存的数据就为2048*2 这会大大占用体系资源 但能有效进步读写速率 不过在嵌入式体系中不建议如许做
别的设置堆栈巨细 越大越好

时间戳函数

在文件fatfs.c中修改时间戳函数
  1. /**
  2.   * @brief  Gets Time from RTC
  3.   * @param  None
  4.   * @retval Time in DWORD
  5.   */
  6. DWORD get_fattime(void)
  7. {
  8.   /* USER CODE BEGIN get_fattime */
  9.         RTC_TimeTypeDef sTime;
  10.         RTC_DateTypeDef sDate;
  11.         //获取RTC时间
  12.         if(HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK)
  13.         {
  14.                 //获取RTC日期
  15.                 HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
  16.                
  17.                 WORD date=(2000+sDate.Year-1980)<<9;
  18.                 date = date |(sDate.Month<<5) |sDate.Date;
  19.                 WORD time=sTime.Hours<<11;
  20.                 time = time | (sTime.Minutes<<5) | (sTime.Seconds>1);
  21.                 DWORD dt=(date<<16) | time;
  22.                
  23.                 return        dt;
  24.         }
  25.         else
  26.                 return 0;
  27.   /* USER CODE END get_fattime */
  28. }
复制代码
文件操作函数

创建一个文件 加上文件操作等函数
头文件声明:
  1. #ifndef FILE_OPERATE_H
  2. #define FILE_OPERATE_H
  3. #include "main.h"
  4. #include "FatFs.h"
  5. #include "stdio.h"
  6. /*函数声明*/
  7. void FatFS_Init(void);
  8. void FatFs_GetDiskInfo(void);
  9. void FatFs_ScanDir(const TCHAR* PathName);
  10. void FatFs_ReadTXTFile(TCHAR *filename);
  11. void FatFs_WriteTXTFile(TCHAR *filename,uint16_t year, uint8_t month, uint8_t day);
  12. void FatFs_GetFileInfo(TCHAR *filename);
  13. void FatFs_DeleteFile(TCHAR *filename);
  14. void FatFs_PrintfFileDate(WORD date, WORD time);
  15. #endif
复制代码
工作区缓存

  1. //定义用于格式化的工作区缓存
  2. BYTE work[_MAX_SS];
复制代码
由于一次只操作一个扇区 所以缓存巨细即为2048
文件挂载和格式化测试

  1.         retUSER = f_mount(&USERFatFS,USERPath,1);//挂载盘符A
  2.         if(retUSER == FR_NO_FILESYSTEM)//没有文件系统就格式化创建创建文件系统
  3.         {
  4.                         retUSER = f_mkfs(USERPath,FM_FAT,2048,work,sizeof(work));
  5.                         if(retUSER == FR_OK)
  6.                         {
  7.                                         retUSER = f_mount(&USERFatFS,USERPath,1);//挂载
  8.                                         printf("[FatFS] 格式化成功retUSER=%d\r\n",retUSER);
  9.                         }
  10.                         else
  11.                         {
  12.                                 printf("[FatFS] 格式化失败retUSER=%d\r\n",retUSER);
  13.                                 return;
  14.                         }//格式化失败
  15.         }
  16.         else if(retUSER == FR_OK)
  17.         {
  18.                 printf("[FatFS] 挂载成功retUSER=%d\r\n",retUSER);
  19.         }
  20.         else
  21.         {
  22.                 printf("[FatFS] 挂载失败retUSER=%d\r\n",retUSER);
  23.                 return;
  24.         }//挂载失败
复制代码
第一次时首先挂载 若未乐成则重新格式化再挂载
需要注意的是 格式化后 Flash内容尽量不要发生改变
若不慎改变 则很大概在挂载时会卡死 建议执行重新格式化
最好的方法就是把首个文件体系扇区举行擦除 然后让函数重新执行格式化
在格式化中 f_mkfs函数的传参除了路径、文件体系类型外 其工作区和工作区巨细 以及分配单位巨细都要与2048对齐
文件读写测试

若挂载乐成 则可以举行文件读写测试
  1. void SDFileTestWrite(void)
  2. {
  3.     FRESULT res_sd;
  4.     UINT fnum;/* 文件成功读写数量 */
  5.     char string[100];
  6.     signed int ByteNum = 0;
  7.     memset(string,0,sizeof(string));
  8.     sprintf(string,"%s%s.xls",USERPath,"Test");
  9.     res_sd = f_open(&USERFile, string,FA_CREATE_ALWAYS | FA_WRITE );
  10.     if(res_sd != FR_OK){printf("[FILE] Failed to create file! %d\r\n",res_sd);}
  11.     sprintf(string,"Vreal\tA1\tA2\n");
  12.     ByteNum = strlen(string);
  13.     res_sd=f_write(&USERFile,string,ByteNum,&fnum);
  14.     res_sd = f_close(&USERFile);
  15.     if(res_sd != FR_OK){printf("[FILE] Error:File closure Exception!\r\n");}
  16.     else{printf("[FILE] SDFileTestWrite ok!\r\n");}
  17. }
  18. void SDFileTestRead(void)
  19. {
  20.     FRESULT res_sd;
  21.     char string[100];
  22.     uint32_t line = 0;
  23.     memset(string,0,sizeof(string));
  24.     sprintf(string,"%s%s.xls",USERPath,"Test");
  25.     res_sd = f_open(&USERFile, string, FA_OPEN_EXISTING | FA_READ);
  26.     if(res_sd != FR_OK){goto LoadFail;}
  27.     line = 0;
  28.     while(!(f_eof(&USERFile)))
  29.     {
  30.         memset(string,0,sizeof(string));
  31.         f_gets(string,sizeof(string),&USERFile);
  32.         if(strlen(string) == 0){break;}
  33.         ++line;
  34.         printf("[FILE] line:%d %s\r\n",line,string);
  35.         //sscanf(string,"%f\t%f\t%f\n",&Vreal[*pNum],&Va1[*pNum],&Va2[*pNum]);//按格式提取字符串函数
  36.     }
  37.     res_sd = f_close(&USERFile);
  38.     if(res_sd != FR_OK){printf("[FILE] Error:Load File closure Exception!\r\n");}
  39.     printf("[FILE] SDFileTestRead ok\r\n");
  40.     return;
  41.     LoadFail:
  42.     {
  43.       printf("[FILE] Load Fail:%s\r\n",string);
  44.     }
  45. }
复制代码
此函数实现了创建一个xls文件 并读取
其他文件操作函数

包罗但不限于 查看目次所有文件、添加/删除文件、文件信息欣赏等等
其实就是C语言文件操作那一些函数罢了 对应在Linux中就是ls、mkdir、touch等等 具体的模拟CLI实现可以用串口来举行
完整代码:
  1. #include "file_operate.h"#include <string.h>//定义用于格式化的工作区缓存
  2. BYTE work[_MAX_SS];
  3. void SDFileTestWrite(void)
  4. {
  5.     FRESULT res_sd;
  6.     UINT fnum;/* 文件成功读写数量 */
  7.     char string[100];
  8.     signed int ByteNum = 0;
  9.     memset(string,0,sizeof(string));
  10.     sprintf(string,"%s%s.xls",USERPath,"Test");
  11.     res_sd = f_open(&USERFile, string,FA_CREATE_ALWAYS | FA_WRITE );
  12.     if(res_sd != FR_OK){printf("[FILE] Failed to create file! %d\r\n",res_sd);}
  13.     sprintf(string,"Vreal\tA1\tA2\n");
  14.     ByteNum = strlen(string);
  15.     res_sd=f_write(&USERFile,string,ByteNum,&fnum);
  16.     res_sd = f_close(&USERFile);
  17.     if(res_sd != FR_OK){printf("[FILE] Error:File closure Exception!\r\n");}
  18.     else{printf("[FILE] SDFileTestWrite ok!\r\n");}
  19. }
  20. void SDFileTestRead(void)
  21. {
  22.     FRESULT res_sd;
  23.     char string[100];
  24.     uint32_t line = 0;
  25.     memset(string,0,sizeof(string));
  26.     sprintf(string,"%s%s.xls",USERPath,"Test");
  27.     res_sd = f_open(&USERFile, string, FA_OPEN_EXISTING | FA_READ);
  28.     if(res_sd != FR_OK){goto LoadFail;}
  29.     line = 0;
  30.     while(!(f_eof(&USERFile)))
  31.     {
  32.         memset(string,0,sizeof(string));
  33.         f_gets(string,sizeof(string),&USERFile);
  34.         if(strlen(string) == 0){break;}
  35.         ++line;
  36.         printf("[FILE] line:%d %s\r\n",line,string);
  37.         //sscanf(string,"%f\t%f\t%f\n",&Vreal[*pNum],&Va1[*pNum],&Va2[*pNum]);//按格式提取字符串函数
  38.     }
  39.     res_sd = f_close(&USERFile);
  40.     if(res_sd != FR_OK){printf("[FILE] Error:Load File closure Exception!\r\n");}
  41.     printf("[FILE] SDFileTestRead ok\r\n");
  42.     return;
  43.     LoadFail:
  44.     {
  45.       printf("[FILE] Load Fail:%s\r\n",string);
  46.     }
  47. }
  48. /*挂载FatFs文件体系*/void FatFS_Init(void){                retUSER = f_mount(&USERFatFS,USERPath,1);//挂载盘符A
  49.         if(retUSER == FR_NO_FILESYSTEM)//没有文件系统就格式化创建创建文件系统
  50.         {
  51.                         retUSER = f_mkfs(USERPath,FM_FAT,2048,work,sizeof(work));
  52.                         if(retUSER == FR_OK)
  53.                         {
  54.                                         retUSER = f_mount(&USERFatFS,USERPath,1);//挂载
  55.                                         printf("[FatFS] 格式化成功retUSER=%d\r\n",retUSER);
  56.                         }
  57.                         else
  58.                         {
  59.                                 printf("[FatFS] 格式化失败retUSER=%d\r\n",retUSER);
  60.                                 return;
  61.                         }//格式化失败
  62.         }
  63.         else if(retUSER == FR_OK)
  64.         {
  65.                 printf("[FatFS] 挂载成功retUSER=%d\r\n",retUSER);
  66.         }
  67.         else
  68.         {
  69.                 printf("[FatFS] 挂载失败retUSER=%d\r\n",retUSER);
  70.                 return;
  71.         }//挂载失败
  72.                 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存储器,小容量        //剩余空间巨细,单位:KB    DWORD freespace= (fre_sect*fs->ssize)>>10;           //总空间巨细,单位:KB    DWORD 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);#endif                    printf("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);#else    printf("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);#else    printf("Free space(KB) = %ld\r\n", freespace);#endif    printf("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");        //打开失败        else                printf("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");        //发生其他错误        else                printf("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);}
复制代码
测试

在格式化前 数据都是FF

在挂载测试时 会读取整个硬盘数据 发现没数据 就会报挂载不乐成 然后开始格式化
格式化时 写入的第一个地址内容如下:

格式化完成后:


这些都是底层操作 我们不用思量 只要文件体系没BUG 就肯定能跑
格式化乐成测试:

在测试之前 我跑了一下Flash Test 其会将0x0808 0000的整个页面清空
所以 每次复位都会重新格式化
去掉Flash Test后则能直接挂载:

硬盘信息:

目次下所有文件信息:

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

SysTick体系定时器精准延时

延时函数

SysTick->LOAD中的值为计数值
计算方法为工作频率值/分频值
好比工作频率/1000 则周期为1ms
以ADuCM4050为例:
  1. #include "ADuCM4050.h"
  2. void delay_ms(unsigned int ms)
  3. {
  4.         SysTick->LOAD = 26000000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
  5.         SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
  6.         SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能52MHz的系统定时器
  7.         while(ms--)
  8.         {
  9.                 while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
  10.         }
  11.         SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
  12. }
  13. void delay_us(unsigned int us)
  14. {
  15.         SysTick->LOAD = 26000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
  16.         SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
  17.         SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能52MHz的系统定时器
  18.         while(us--)
  19.         {
  20.                 while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
  21.         }
  22.         SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
  23. }
复制代码
其中的52000000表现芯片的体系定时器频率 32系列一样平常为外部定时器频率的两倍
Cortex-M架构SysTick体系定时器壅闭和非壅闭延时
壅闭延时

首先是最常用的壅闭延时
  1. void delay_ms(unsigned int ms)
  2. {
  3.         SysTick->LOAD = 50000000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
  4.         SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
  5.         SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
  6.         while(ms--)
  7.         {
  8.                 while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
  9.         }
  10.         SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
  11. }
  12. void delay_us(unsigned int us)
  13. {
  14.         SysTick->LOAD = 50000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
  15.         SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
  16.         SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
  17.         while(us--)
  18.         {
  19.                 while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
  20.         }
  21.         SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
  22. }
复制代码
50000000表现工作频率
分频后即可得到不同的延时时间
以此类推
那么 不用两个嵌套while循环 也可以写成:
  1. void delay_ms(unsigned int ms)
  2. {
  3.         SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
  4.         SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
  5.         SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
  6.         while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
  7.         SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
  8. }
  9. void delay_us(unsigned int us)
  10. {
  11.         SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
  12.         SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
  13.         SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
  14.        
  15.         while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
  16.         SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
  17. }
复制代码
但是这种写法有个毛病
那就是输入ms后,最大定时不得凌驾计数值,也就是不能凌驾LOAD的最大值,否则溢出以后,则无法正常工作
而LOAD假如最大是32位 也就是4294967295
晶振为50M的话 50M的计数值为1s 4294967295计数值约为85s
固最大定时时间为85s
但用嵌套while的话 最大可以支持定时4294967295*85s
非壅闭延时

假如采用非壅闭的话 直接改写第二种方法就好了:
  1. void delay_ms(unsigned int ms)
  2. {
  3.         SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
  4.         SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
  5.         SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
  6.         //while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
  7.         //SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
  8. }
  9. void delay_us(unsigned int us)
  10. {
  11.         SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
  12.         SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
  13.         SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
  14.        
  15.         //while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
  16.         //SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
  17. }
复制代码
将等待和关闭定时器语句去掉
在利用时加上判定即可变为壅闭:
  1. delay_ms(500);
  2. while ((SysTick->CTRL & 0x00010000)==0);
  3. SysTick->CTRL = 0;
复制代码
在非壅闭状态下 可以提交定时器后 去做别的事情 然后再来等待
不过如许又有一个毛病 那就是定时器会主动重载 大概做别的事情以后 定时器跑过了 然后就要等85s才能停下
故可以通过内部定时器来举行非壅闭延时函数的编写
基本上每个mcu的内部定时器都可以设置主动重载等功能 网上资料很多 这里就不再阐述了
位带操作

位带代码

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

  1. #ifndef __GPIO_H__
  2. #define __GPIO_H__
  3. #include "ADuCM4050.h"
  4. #include "adi_gpio.h"
  5. #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
  6. #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
  7. #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
  8. #define GPIO0_ODR_Addr    (ADI_GPIO0_BASE+20) //0x40020014
  9. #define GPIO0_IDR_Addr    (ADI_GPIO0_BASE+16) //0x40020010
  10. #define GPIO1_ODR_Addr    (ADI_GPIO1_BASE+20) //0x40020054
  11. #define GPIO1_IDR_Addr    (ADI_GPIO1_BASE+16) //0x40020050
  12. #define GPIO2_ODR_Addr    (ADI_GPIO2_BASE+20) //0x40020094
  13. #define GPIO2_IDR_Addr    (ADI_GPIO2_BASE+16) //0x40020090
  14. #define GPIO3_ODR_Addr    (ADI_GPIO3_BASE+20) //0x400200D4
  15. #define GPIO3_IDR_Addr    (ADI_GPIO3_BASE+16) //0x400200D0
  16. #define P0_O(n)           BIT_ADDR(GPIO0_ODR_Addr,n)  //输出
  17. #define P0_I(n)            BIT_ADDR(GPIO0_IDR_Addr,n)  //输入
  18. #define P1_O(n)           BIT_ADDR(GPIO1_ODR_Addr,n)  //输出
  19. #define P1_I(n)            BIT_ADDR(GPIO1_IDR_Addr,n)  //输入
  20. #define P2_O(n)           BIT_ADDR(GPIO2_ODR_Addr,n)  //输出
  21. #define P2_I(n)            BIT_ADDR(GPIO2_IDR_Addr,n)  //输入
  22. #define P3_O(n)           BIT_ADDR(GPIO3_ODR_Addr,n)  //输出
  23. #define P3_I(n)            BIT_ADDR(GPIO3_IDR_Addr,n)  //输入
  24. #define Port0                        (ADI_GPIO_PORT0)
  25. #define Port1                        (ADI_GPIO_PORT1)
  26. #define Port2                        (ADI_GPIO_PORT2)
  27. #define Port3                        (ADI_GPIO_PORT3)
  28. #define Pin0                        (ADI_GPIO_PIN_0)
  29. #define Pin1                        (ADI_GPIO_PIN_1)
  30. #define Pin2                        (ADI_GPIO_PIN_2)
  31. #define Pin3                        (ADI_GPIO_PIN_3)
  32. #define Pin4                        (ADI_GPIO_PIN_4)
  33. #define Pin5                        (ADI_GPIO_PIN_5)
  34. #define Pin6                        (ADI_GPIO_PIN_6)
  35. #define Pin7                        (ADI_GPIO_PIN_7)
  36. #define Pin8                        (ADI_GPIO_PIN_8)
  37. #define Pin9                        (ADI_GPIO_PIN_9)
  38. #define Pin10                        (ADI_GPIO_PIN_10)
  39. #define Pin11                        (ADI_GPIO_PIN_11)
  40. #define Pin12                        (ADI_GPIO_PIN_12)
  41. #define Pin13                        (ADI_GPIO_PIN_13)
  42. #define Pin14                        (ADI_GPIO_PIN_14)
  43. #define Pin15                        (ADI_GPIO_PIN_15)
  44. void GPIO_OUT(unsigned int port,unsigned int pin,unsigned int flag);
  45. void GPIO_BUS_OUT(unsigned int port,unsigned int num);
  46. void P0_BUS_O(unsigned int num);
  47. unsigned int P0_BUS_I(void);
  48. void P1_BUS_O(unsigned int num);
  49. unsigned int P1_BUS_I(void);
  50. void P2_BUS_O(unsigned int num);
  51. unsigned int P2_BUS_I(void);
  52. void P3_BUS_O(unsigned int num);
  53. unsigned int P3_BUS_I(void);
  54. #endif
复制代码
总线函数

  1. #include "ADuCM4050.h"
  2. #include "adi_gpio.h"
  3. #include "GPIO.h"
  4. void GPIO_OUT(unsigned int port,unsigned int pin,unsigned int flag)
  5. {
  6.         switch(port)
  7.         {
  8.                 case 0:{
  9.                         switch(pin)
  10.                         {
  11.                                 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;
  12.                                 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;
  13.                                 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;
  14.                                 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;
  15.                                 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;
  16.                                 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;
  17.                                 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;
  18.                                 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;
  19.                                 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;
  20.                                 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;
  21.                                 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;
  22.                                 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;
  23.                                 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;
  24.                                 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;
  25.                                 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;
  26.                                 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;
  27.                                 default:pin=0;break;
  28.                         }
  29.                 }break;
  30.                
  31.                 case 1:{
  32.                         switch(pin)
  33.                         {
  34.                                 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;
  35.                                 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;
  36.                                 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;
  37.                                 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;
  38.                                 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;
  39.                                 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;
  40.                                 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;
  41.                                 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;
  42.                                 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;
  43.                                 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;
  44.                                 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;
  45.                                 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;
  46.                                 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;
  47.                                 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;
  48.                                 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;
  49.                                 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;
  50.                                 default:pin=0;break;
  51.                         }
  52.                 }break;
  53.                
  54.                 case 2:{
  55.                         switch(pin)
  56.                         {
  57.                                 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;
  58.                                 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;
  59.                                 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;
  60.                                 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;
  61.                                 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;
  62.                                 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;
  63.                                 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;
  64.                                 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;
  65.                                 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;
  66.                                 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;
  67.                                 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;
  68.                                 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;
  69.                                 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;
  70.                                 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;
  71.                                 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;
  72.                                 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;
  73.                                 default:pin=0;break;
  74.                         }
  75.                 }break;
  76.                
  77.                 case 3:{
  78.                         switch(pin)
  79.                         {
  80.                                 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;
  81.                                 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;
  82.                                 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;
  83.                                 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;
  84.                                 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;
  85.                                 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;
  86.                                 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;
  87.                                 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;
  88.                                 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;
  89.                                 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;
  90.                                 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;
  91.                                 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;
  92.                                 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;
  93.                                 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;
  94.                                 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;
  95.                                 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;
  96.                                 default:pin=0;break;
  97.                         }
  98.                 }break;
  99.                
  100.                 default:port=0;break;
  101.         }       
  102. }
  103. void GPIO_BUS_OUT(unsigned int port,unsigned int num)  //num最大为0xffff
  104. {
  105.         int i;
  106.         for(i=0;i<16;i++)
  107.         {
  108.                 GPIO_OUT(port,i,(num>>i)&0x0001);
  109.         }
  110. }
  111. void P0_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
  112. {
  113.         int i;
  114.         for(i=0;i<16;i++)
  115.         {
  116.                 P0_O(i)=(num>>i)&0x0001;
  117.         }
  118. }
  119. unsigned int P0_BUS_I(void)  //输出值num最大为0xFFFF
  120. {
  121.         unsigned int num;
  122.         int i;
  123.         for(i=0;i<16;i++)
  124.         {
  125.                 num=num+(P0_I(i)<<i)&0xFFFF;
  126.         }
  127.         return num;
  128. }
  129. void P1_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
  130. {
  131.         int i;
  132.         for(i=0;i<16;i++)
  133.         {
  134.                 P1_O(i)=(num>>i)&0x0001;
  135.         }
  136. }
  137. unsigned int P1_BUS_I(void)  //输出值num最大为0xFFFF
  138. {
  139.         unsigned int num;
  140.         int i;
  141.         for(i=0;i<16;i++)
  142.         {
  143.                 num=num+(P1_I(i)<<i)&0xFFFF;
  144.         }
  145.         return num;
  146. }
  147. void P2_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
  148. {
  149.         int i;
  150.         for(i=0;i<16;i++)
  151.         {
  152.                 P2_O(i)=(num>>i)&0x0001;
  153.         }
  154. }
  155. unsigned int P2_BUS_I(void)  //输出值num最大为0xFFFF
  156. {
  157.         unsigned int num;
  158.         int i;
  159.         for(i=0;i<16;i++)
  160.         {
  161.                 num=num+(P2_I(i)<<i)&0xFFFF;
  162.         }
  163.         return num;
  164. }
  165. void P3_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
  166. {
  167.         int i;
  168.         for(i=0;i<16;i++)
  169.         {
  170.                 P3_O(i)=(num>>i)&0x0001;
  171.         }
  172. }
  173. unsigned int P3_BUS_I(void)  //输出值num最大为0xFFFF
  174. {
  175.         unsigned int num;
  176.         int i;
  177.         for(i=0;i<16;i++)
  178.         {
  179.                 num=num+(P3_I(i)<<i)&0xFFFF;
  180.         }
  181.         return num;
  182. }
复制代码
一、位带操作理论及实践

位带操作的概念其实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引脚的话:
  1. uint32_t *PF9_BitBand =
  2. *(uint32_t *)(0x42000000 + ((uint32_t )&GPIOF->ODR– 0x40000000) *32 + 9*4)
复制代码
封装一下:
  1. #define PFout(x) *(volatile uint32_t *)(0x42000000 + ((uint32_t )&GPIOF->ODR – 0x40000000) *32 + x*4)
复制代码
现在 可以把通用部分封装成一个小定义:
  1. #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
  2. #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
  3. #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
复制代码
那么 设置PF引脚的函数可以定义:
  1. #define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414   
  2. #define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410
  3. #define PF_O(n)           BIT_ADDR(GPIOF_ODR_Addr,n)  //输出
  4. #define PF_I(n)            BIT_ADDR(GPIOF_IDR_Addr,n)  //输入
复制代码
若使PF9输入输出则:
  1. PF_O(9)=1;  //输出高电平
  2. uint8_t dat = PF_I(9);  //获取PF9引脚的值
复制代码
总线输入输出:
  1. void PF_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
  2. {
  3.         int i;
  4.         for(i=0;i<16;i++)
  5.         {
  6.                 PF_O(i)=(num>>i)&0x0001;
  7.         }
  8. }
  9. unsigned int PF_BUS_I(void)  //输出值num最大为0xFFFF
  10. {
  11.         unsigned int num;
  12.         int i;
  13.         for(i=0;i<16;i++)
  14.         {
  15.                 num=num+(PF_I(i)<<i)&0xFFFF;
  16.         }
  17.         return num;
  18. }
复制代码
STM32的可用下面的函数:
  1. #ifndef __GPIO_H__#define __GPIO_H__#include "stm32l496xx.h"#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
  2. #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
  3. #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
  4. #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
复制代码
  1. #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
  2. {
  3.         int i;
  4.         for(i=0;i<16;i++)
  5.         {
  6.                 PF_O(i)=(num>>i)&0x0001;
  7.         }
  8. }
  9. unsigned int PF_BUS_I(void)  //输出值num最大为0xFFFF
  10. {
  11.         unsigned int num;
  12.         int i;
  13.         for(i=0;i<16;i++)
  14.         {
  15.                 num=num+(PF_I(i)<<i)&0xFFFF;
  16.         }
  17.         return num;
  18. }
  19. 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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

河曲智叟

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表