【雅特力AT32】串口UART、USART配置和使用方法,數組的阻塞發送函數編寫,串口接收中斷、回環、重定向
文章目錄
- 串口配置
- 阻塞發送函數
- 接收中斷和串口回環
- 串口重定向
- 附錄:Cortex-M架構的SysTick系統定時器精準延時和MCU位帶操作
- SysTick系統定時器精準延時
- 延時函數
- 阻塞延時
- 非阻塞延時
- 位帶操作
- 位帶代碼
- 位帶宏定義
- 總線函數
- 一、位帶操作理論及實踐
- 二、如何判斷MCU的外設是否支持位帶
串口配置
類似于CubeMX 甚至還告訴你了數據位包括校驗位 比較友好
如果要中斷的話 需要在NVIC中開啟
生成的配置代碼如下:
/*** @brief init uart5 function* @param none* @retval none*/
void wk_uart5_init(void)
{/* add user code begin uart5_init 0 *//* add user code end uart5_init 0 */gpio_init_type gpio_init_struct;gpio_default_para_init(&gpio_init_struct);/* add user code begin uart5_init 1 *//* add user code end uart5_init 1 *//* configure the TX pin */gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;gpio_init_struct.gpio_mode = GPIO_MODE_MUX;gpio_init_struct.gpio_pins = LCD_UART_TX_PIN;gpio_init_struct.gpio_pull = GPIO_PULL_NONE;gpio_init(LCD_UART_TX_GPIO_PORT, &gpio_init_struct);/* configure the RX pin */gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;gpio_init_struct.gpio_pins = LCD_UART_RX_PIN;gpio_init_struct.gpio_pull = GPIO_PULL_UP;gpio_init(LCD_UART_RX_GPIO_PORT, &gpio_init_struct);gpio_pin_remap_config(UART5_GMUX_0001, TRUE);/* configure param */usart_init(UART5, 115200, USART_DATA_9BITS, USART_STOP_1_BIT);usart_transmitter_enable(UART5, TRUE);usart_receiver_enable(UART5, TRUE);usart_parity_selection_config(UART5, USART_PARITY_EVEN);usart_hardware_flow_control_set(UART5, USART_HARDWARE_FLOW_NONE);/*** Users need to configure UART5 interrupt functions according to the actual application.* 1. Call the below function to enable the corresponding UART5 interrupt.* --usart_interrupt_enable(...)* 2. Add the user's interrupt handler code into the below function in the at32f403a_407_int.c file.* --void UART5_IRQHandler(void)*/usart_enable(UART5, TRUE);/* add user code begin uart5_init 2 *//* add user code end uart5_init 2 */
}
其中
usart_transmitter_enable(UART5, TRUE);usart_receiver_enable(UART5, TRUE);
分別為開啟發送和接收功能 徒增功耗罷了
另外 AT32的配置特別有意思
就是當你的GPIO口為輸出功能時(包括串口TX等等)
是無法配置上下拉的
那么就需要手動配置一遍
另外 如果開啟了中斷 則通過usart_interrupt_enable函數來指定開啟哪一種中斷 這里我開啟接收中斷
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;gpio_init_struct.gpio_mode = GPIO_MODE_MUX;gpio_init_struct.gpio_pins = LCD_UART_TX_PIN;gpio_init_struct.gpio_pull = GPIO_PULL_UP;gpio_init(LCD_UART_TX_GPIO_PORT, &gpio_init_struct);usart_interrupt_enable(UART5,USART_RDBF_INT,TRUE);
阻塞發送函數
串口的數據寄存器長這樣
這個是發送和接收共同的寄存器
發送就是往這個里面寫值 接收就是讀值
庫函數為:
/*** @brief transmit single data through the usart peripheral.* @param usart_x: select the usart or the uart peripheral.* this parameter can be one of the following values:* USART1, USART2, USART3, UART4, UART5, USART6, UART7 or UART8.* @param data: the data to transmit.* @retval none*/
void usart_data_transmit(usart_type* usart_x, uint16_t data)
{usart_x->dt = (data & 0x01FF);
}/*** @brief return the most recent received data by the usart peripheral.* @param usart_x: select the usart or the uart peripheral.* this parameter can be one of the following values:* USART1, USART2, USART3, UART4, UART5, USART6, UART7 or UART8.* @retval the received data.*/
uint16_t usart_data_receive(usart_type* usart_x)
{return (uint16_t)(usart_x->dt);
}
可以看到 其都為非阻塞的
所以在進行發送時 需要進行判斷保證數據發送成功
按官方的寫法是:
while(usart_flag_get(usart_x, USART_TDBE_FLAG) == RESET);usart_data_transmit(usart_x, (uint16_t)data[i]);while(usart_flag_get(usart_x, USART_TDC_FLAG) == RESET);
先等待發送BUF為空 然后進行發送 最后等待發送完成
但其實如果發送完成的話 肯定是為空的
另外 由于接收和發送共用寄存器 所以如果當數據剛被接收時 就算USART_TDBE_FLAG
置位 發送數據也發不出來
(特別是在接收中斷中 接收還沒完全跑完 所以可能會認定寄存器非空 雖然不建議在中斷中調用阻塞函數)
所以改進函數為:
void UART_Write_Blocking(usart_type* usart_x, uint8_t* data,uint16_t size)
{uint16_t i=0;for(i=0;i<size;i++){usart_data_transmit(usart_x, (uint16_t)data[i]);while(usart_flag_get(usart_x, USART_TDC_FLAG) == RESET);usart_flag_clear(usart_x,USART_TDBE_FLAG);}
}
這樣也能發數組
接收中斷和串口回環
一般串口采用中斷的方式接收 也就是非阻塞 如果用阻塞 可以仿照上面的函數來寫
接收中斷產生時 會跑到中斷服務函數中
/*** @brief this function handles UART4 handler.* @param none* @retval none*/
void UART4_IRQHandler(void)
{/* add user code begin UART4_IRQ 0 */if(usart_interrupt_flag_get(DEBUG_UART_Handle, USART_RDBF_FLAG) != RESET){usart_flag_clear(DEBUG_UART_Handle,USART_RDBF_FLAG);/* read one byte from the receive data register */RxBuffer = usart_data_receive(LCD_UART_Handle); UART_Write_Blocking(LCD_UART_Handle,&RxBuffer,1);}/* add user code end UART4_IRQ 0 *//* add user code begin UART4_IRQ 1 *//* add user code end UART4_IRQ 1 */
}
采用usart_interrupt_flag_get
函數判斷是否產生了接收中斷
然后清空標識位
直接采用usart_data_receive
函數即可讀到數據
這里直接在中斷中使用阻塞發送函數即可實現回環(不建議)
如果不使用阻塞發送的話 假如第二次接收中斷產生 則可能因為速度過快導致上一次的還沒發送完 可能就Error了
最好的解決方案是 在主函數中實現發送 接收新數據后 搞個狀態機置位
然后在輪詢狀態機進行發送
串口重定向
沒啥好配置的
都一樣
就是printf
這里要用阻塞發送函數
#pragma import(__use_no_semihosting_swi)
struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
FILE __stdin;
void _sys_exit(int x)
{ x = x;
}
void _ttywrch(int ch)
{ch = ch;
}void UART_Write_Blocking(usart_type* usart_x, uint8_t* data,uint16_t size)
{uint16_t i=0;for(i=0;i<size;i++){usart_data_transmit(usart_x, (uint16_t)data[i]);while(usart_flag_get(usart_x, USART_TDC_FLAG) == RESET);usart_flag_clear(usart_x,USART_TDBE_FLAG);}
}int fputc(int ch, FILE *f)
{UART_Write_Blocking(DEBUG_UART_Handle,(uint8_t *)&ch,1);
// HAL_UART_Transmit(&DEBUG_UART_Handle,(uint8_t *)&ch,1,0xFFFF);
// HAL_UART_Transmit(&LCD_UART_Handle,(uint8_t *)&ch,1,0xFFFF);return ch;
}
附錄: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