【STM32】HAL庫Host MSC讀寫外部U盤及FatFS文件系統的USB Disk模式

【STM32】HAL庫Host MSC讀寫外部U盤及FatFS文件系統的USB Disk模式

在先前 分別介紹了FatFS文件系統和USB虛擬U盤MSC配置
前者通過MCU讀寫Flash建立文件系統 后者通過MSC連接電腦使其能夠被操作
這兩者可以合起來 就能夠實現同時在MCU、USB中操作Flash的文件系統
【STM32】通過L496的HAL庫Flash建立FatFS文件系統(CubeMX自動配置R0.12C版本)
【STM32】HAL庫USB虛擬U盤MSC配置及采用自帶的Flash作為文件系統
以及Flash+MSC的模式
【STM32】通過HAL庫Flash建立FatFS文件系統并配置為USB虛擬U盤MSC

在上面 USB還是工作在Device模式 而不是Host模式 如果配置成Host模式則可以解鎖FatFS中的USB Disk功能 實現方式不同 原理、功能也不一樣 請勿混淆
另外在Host模式下 MCU作為主機使用 可以讀寫外部插入進來的USB硬盤系統
在硬件上需要多加一根線 另外FatFS的USB Disk功能需要連上USB以后才能掛載硬盤
其相關配置可以由CubeMX完全生成 用戶只需要調用文件操作函數API即可 不需要修改代碼

該FatFS模式可以同其他文件系統一起使用 互相獨立不沖突(如普通模式User-defined模式)

文章目錄

  • USB Host MSC
  • FatFS USB Disk
  • 修改代碼
  • 測試
  • 附錄:Cortex-M架構的SysTick系統定時器精準延時和MCU位帶操作
    • SysTick系統定時器精準延時
      • 延時函數
        • 阻塞延時
        • 非阻塞延時
    • 位帶操作
      • 位帶代碼
        • 位帶宏定義
        • 總線函數
      • 一、位帶操作理論及實踐
      • 二、如何判斷MCU的外設是否支持位帶

USB Host MSC

在Host主機模式下 可以配置主機MSC
USB配置為:
在這里插入圖片描述
在該模式下 USB的中斷被強制打開
MSC配置為:
在這里插入圖片描述
這里需要多加一根輸出引腳作為Host控制引腳
在這里插入圖片描述
在工程上 導入Host下的.c文件:
在這里插入圖片描述
另外添加頭文件路徑:
在這里插入圖片描述

.\RTE\Device\STM32L496RGTx\STCubeGenerated\Middlewares\ST\STM32_USB_Host_Library\Class\MSC\Inc
.\RTE\Device\STM32L496RGTx\STCubeGenerated\Middlewares\ST\STM32_USB_Host_Library\Core\Inc

并添加src文件
在這里插入圖片描述
其中 usbh_diskio.c會在配置FatFS后生成

FatFS USB Disk

與其他文件系統配置方式一致
注意支持中文
在這里插入圖片描述
在MAX_SS中 可以根據實際 填入可以支持的硬盤分配單元大小(最大為4096 所以如果硬盤只支持4096 那么這一項必須要為最大 如果硬盤最小超過4096 則MCU無法支持)
同樣導入FatFS相關.c文件
在這里插入圖片描述
以及fatfs.cusbh_diskio.c

在這里插入圖片描述

修改代碼

usbh_diskio.c中 生成的代碼已經完成了文件系統的底層 所以不用我們去修改了
我們需要建立自己的文件操作API函數
如下:

#include "file_operate.h"
#include <string.h>//定義用于格式化的工作區緩存
BYTE work[_MAX_SS];
#define USERFile USBHFile
#define USERFatFS USBHFatFS
#define USERPath USBHPath
#define retUSER retUSBHvoid 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);
}

實際上就是將之前的文件系統介紹的操作函數中的變量給替換了
替換的變量在fatfs.c中可以找到

uint8_t retUSBH;    /* Return value for USBH */
char USBHPath[4];   /* USBH logical drive path */
FATFS USBHFatFS;    /* File system object for USBH logical drive */
FIL USBHFile;       /* File object for USBH */#define USERFile USBHFile
#define USERFatFS USBHFatFS
#define USERPath USBHPath
#define retUSER retUSBH

注意 工作區和堆棧也要與其對應 相應增大 格式化的f_mkfs函數參數也要改變

另外 在USB設備插入之前 會循環調用MX_USB_HOST_Process函數
以輪詢USB設備狀態
在這里插入圖片描述
聲明外部變量Appli_state以隨時可以讀取狀態
(具體在哪定義都可 只要全局包含就行)

extern ApplicationTypeDef Appli_state;

在主循環中可以改成這樣:

while (1){/* USER CODE END WHILE */MX_USB_HOST_Process();/* USER CODE BEGIN 3 */if (Appli_state == APPLICATION_READY){break;}}FatFS_Init();while (1){}

即輪詢USB Disk連接成功后 執行FatFS初始化函數 即掛載、格式化、測試等等

測試

輪詢USB Disk連接成功后:
在這里插入圖片描述
硬盤信息:
在這里插入圖片描述
文件內容:
在這里插入圖片描述

附錄: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.pswp.cn/news/895335.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895335.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895335.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

1.1計算機的發展

一、計算機系統的概念 1、計算機系統軟件&#xff0b;硬件 軟件&#xff1a;由具有各種特殊功能的程序組成。 硬件&#xff1a;計算機的實體。如&#xff1a;主機、外設等。 硬件決定了計算機系統的上限&#xff0c;軟件決定了硬件性能發揮了多少。 2、軟件 軟件有系統軟…

本地生活服務平臺開發進入發展熱潮

本地生活服務平臺&#xff1a;當下的發展熱潮 本地生活服務平臺開發模式 在當今數字化時代&#xff0c;本地生活服務平臺開發已成為人們日常生活中不可或缺的一部分。只需動動手指&#xff0c;打開手機上的 APP&#xff0c;就能輕松滿足各類生活需求。像某團、餓XX這樣的平臺&a…

LSTM變種模型

GRU GRU簡介 門控循環神經網絡 (Gated Recurrent Neural Network&#xff0c;GRNN) 的提出&#xff0c;旨在更好地捕捉時間序列中時間步距離較大的依賴關系。它通過可學習的門來控制信息的流動。其中&#xff0c;門控循環單元 (Gated Recurrent Unit &#xff0c; GRU) 是…

詳解tensorflow的tensor和Python list及Numpy矩陣的區別

TensorFlow中的張量&#xff08;tensor&#xff09;、Python列表和NumPy矩陣在數據結構和功能上有一些顯著的區別。以下是它們的詳細介紹及代碼示例。 1、Python List 定義&#xff1a;Python列表是一種內置的數據結構&#xff0c;可以存儲不同類型的對象&#xff0c;包括數字…

多模態模型詳解

多模態模型是什么 多模態模型是一種能夠處理和理解多種數據類型&#xff08;如文本、圖像、音頻、視頻等&#xff09;的機器學習模型&#xff0c;通過融合不同模態的信息來提升任務的性能。其核心在于利用不同模態之間的互補性&#xff0c;增強模型的魯棒性和準確性。 如何融合…

微服務與網關

什么是網關 背景 單體項目中&#xff0c;前端只用訪問指定的一個端口8080&#xff0c;就可以得到任何想要的數據 微服務項目中&#xff0c;ip是不斷變化的&#xff0c;端口是多個的 解決方案&#xff1a;網關 網關&#xff1a;就是網絡的關口&#xff0c;負責請求的路由、轉發…

二分算法篇:二分答案法的巧妙應用

二分算法篇&#xff1a;二分答案法的巧妙應用 那么看到二分這兩個字想必我們一定非常熟悉&#xff0c;那么在大學期間的c語言的教學中會專門講解二分查找&#xff0c;那么我們來簡單回顧一下二分查找算法&#xff0c;我們知道二分查找是在一個有序的序列中尋找一個數在這個序列…

XZ_Mac電腦上本地化部署DeepSeek的詳細步驟

根據您的需求&#xff0c;以下是Mac電腦上本地化部署DeepSeek的詳細步驟&#xff1a; 一、下載并安裝Ollama 訪問Ollama官網&#xff1a; 打開瀏覽器&#xff0c;訪問 Ollama官網。 下載Ollama&#xff1a; 在官網中找到并點擊“Download”按鈕&#xff0c;選擇適合Mac系統的…

C# OpenCV機器視覺:模仿Halcon各向異性擴散濾波

在一個充滿創意與挑戰的圖像處理工作室里&#xff0c;阿強是一位熱情的圖像魔法師。他總是在追求更加出色的圖像效果&#xff0c;然而&#xff0c;傳統的圖像處理方法有時候并不能滿足他的需求。 有一天&#xff0c;阿強聽說了 Halcon 中的各向異性擴散濾波功能&#xff0c;它…

實現:多活的基礎中間件

APIRouter &#xff1a; 路由分發服務 API Router 是一個 HTTP 反向代理和負載均衡器&#xff0c;部署在公有云中作為 HTTP API 流量的入口&#xff0c;它能識別 出流量的歸屬 shard &#xff0c;并根據 shard 將流量轉發到對應的 ezone 。 API Router 支持多種路由鍵&am…

Python3連接MongoDB并寫入數據

個人博客地址&#xff1a;Python3連接MongoDB并寫入數據 | 一張假鈔的真實世界 安裝PyMongo $ pip3 install pymongo Successfully installed pymongo-3.7.2 連接MongoDB并且批量插入操作 #!/usr/bin/python3import mysql.connector import gzip import json from pymongo …

Python 操作 MongoDB 教程

一、引言 在當今數字化時代&#xff0c;數據的存儲和管理至關重要。傳統的關系型數據庫在處理一些復雜場景時可能會顯得力不從心&#xff0c;而 NoSQL 數據庫應運而生。MongoDB 作為一款開源的、面向文檔的 NoSQL 數據庫&#xff0c;憑借其高性能、高可擴展性和靈活的數據模型…

使用 Python-pptx 庫提取 PPTX 文件中的結構與文字

是的&#xff0c;使用 python-pptx 庫是提取 PPTX 文件中結構和文字的理想選擇&#xff0c;原因如下&#xff1a; 專門處理 PPTX 格式 python-pptx 是一個專門為處理 PPTX 文件&#xff08;.pptx 格式&#xff09;而設計的 Python 庫。 它可以讀取和操作 PPTX 文件的內部結構…

DeepSeek本地化部署

DeepSeek本地化部署 本教程為一鍵式部署&#xff0c;適合于mac、ubuntu、windows。【開源地址】 環境要求 nodejs > 18Python > 3.10.12 步驟一&#xff1a;安裝ollama客戶端 官網直接安裝&#xff0c;ollama官網。安裝完成后使用命令&#xff1a;ollama -h&#xf…

驅動開發系列34 - Linux Graphics Intel 動態顯存技術的實現

一:概述 動態顯存技術(Dynamic Video Memory Technology, DVMT)是一種由 Intel 提出的內存分配技術,主要用于整合顯卡(集成顯卡)系統中,以便動態地調整顯存大小,從而在不同的負載場景下優化內存使用和系統性能。 動態顯存技術的核心在于共享系統內存。集成顯卡沒有獨立…

DeepSeek 入駐 Cursor —— 表現能否超越 Claude?

DeepSeek 剛剛在 Cursor 平臺上線了它的兩款模型&#xff1a;DeepSeek V3 和 R1。目前&#xff0c;許多開發者&#xff08;包括我們在內&#xff09;主要依賴 Claude 3.5 Sonnet&#xff08;最新版本 claude-3-5-sonnet-20241022&#xff09;作為主要語言模型&#xff0c;因此我…

持久性HTTPVS.非持久性HTTP

1. HTTP協議基礎 HTTP&#xff08;HyperText Transfer Protocol&#xff09;是Web通信的核心協議&#xff0c;定義了客戶端&#xff08;瀏覽器&#xff09;與服務器之間傳輸數據的規則。 在HTTP/1.0及之前的版本中&#xff0c;默認使用非持久性連接&#xff0c;而HTTP/1.1及更…

大數據與大模型:數字時代的共生力量

引言&#xff1a;大數據與大模型的嶄新時代 在數字化浪潮洶涌澎湃的當下&#xff0c;大數據與大模型無疑是最為耀眼的兩顆明星 &#xff0c;深刻地改變著我們的生活、工作和思維方式。大數據&#xff0c;作為信息時代的寶藏&#xff0c;蘊含著無盡的價值。從電商平臺的海量交易…

中間件-redis-(ubantu)

1、安裝依賴包 sudo apt-get update sudo apt-get install redis 一旦安裝完成&#xff0c;Redis 服務將會自動啟動。想要檢查服務的狀態&#xff0c;輸入下面的命令&#xff1a; rootvims:/etc/redis# sudo systemctl status redis-server ● redis-server.service - Adva…

網絡安全架構師怎么考 網絡安全 架構

安全通信網絡 隨著現代技術的不斷發展&#xff0c;等級保護對象通常通過網絡實現資源共享和數據交互&#xff0c;當大量的設備連成網絡后&#xff0c;網絡安全成了最為關注的問題。按照“一個中心&#xff0c;三重防御”的縱深防御思想&#xff0c;邊界外部通過廣域網或城域網…