【雅特力AT32】串口UART、USART配置和使用方法,數組的阻塞發送函數編寫,串口接收中斷、回環、重定向

【雅特力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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/14166.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/14166.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/14166.shtml

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

相關文章

了解CentOS及其基礎

什么是CentOS&#xff1f; CentOS&#xff08;Community ENTerprise Operating System&#xff09;是一個社區支持的操作系統&#xff0c;基于Red Hat Enterprise Linux&#xff08;RHEL&#xff09;的源代碼。它是免費且開源的&#xff0c;廣泛用于服務器環境。 為什么選擇C…

深入理解HTTP:工作原理、特性及應用

HTTP&#xff0c;全稱Hypertext Transfer Protocol&#xff0c;即超文本傳輸協議&#xff0c;是互聯網上應用最為廣泛的一種網絡協議。它定義了客戶端和服務器之間如何交換信息&#xff0c;以及如何傳輸超文本和其他內容。HTTP協議的出現極大地推動了互聯網的普及和發展&#x…

ARM|DSP+FPGA+NVIDIA AI攝像頭定制

信邁擁有高性能的攝像頭全棧能力&#xff1a;掌握車載模組光學設計能力&#xff0c;具有多名經驗豐富光學設計專家&#xff1b;具備豐富的車載攝像模組硬件設計經驗&#xff1b;掌握目前市面上大部分車載平臺的ISP圖像畫質服務能力&#xff0c;能自主開發圖像ISP和增強算法&…

第十二章 網絡編程

第十二章 網絡編程 網絡協議概述 通信協議&#xff1a; 協議即規則&#xff0c;就好比汽車上路要遵守交通規則一樣&#xff0c;為了使全世界不同類型的計算機都可以連接起來&#xff0c;所以制定了一套全球通用的通信協議——Internet協議。有了Internet協議&#xff0c;任何…

【mysql】【docker】mysql8-互為主從

&#x1f338;&#x1f338; Linux/docker-compose/mysql8 互為主從 優雅部署 &#x1f338;&#x1f338; 記錄下兩臺Linux的mysql需要熱備份&#xff0c;互為主從&#xff0c;后期加上keepalived實現高可用切換 參考博客&#xff1a;答 案 &#x1f338; 一、準備文件 這里…

圖形學初識--直線插值算法

文章目錄 為什么需要插值算法&#xff1f;插值算法是什么&#xff1f;有哪些常見的插值算法呢&#xff1f;1. 線性插值&#xff08;Linear Interpolation&#xff09;2. 多項式插值&#xff08;Polynomial Interpolation&#xff09;3. 樣條插值&#xff08;Spline Interpolati…

Blazor 下支持 Azure AD 的多套登錄方案

比如上圖配置了兩套不同的登錄方案&#xff0c;各有自己的 TenantId 和 ClientId &#xff0c;要同時支持他們的登錄&#xff08;其實在同一套 TenantId 和 ClientId 里面配置多個登錄賬戶不就好了&#xff0c;但是......那套登錄的管理是在客戶自己的Azure AD賬戶管理下的&…

SpringBoot3 解決NoResourceFoundException: No static resource favicon.ico.異常

SpringBoot3 解決NoResourceFoundException: No static resource favicon.ico.異常 spring boot3項目中瀏覽器中訪問報錯找不到favicon.ico&#xff0c;雖然不影響使用&#xff0c;用api工具也可以忽略這個異常&#xff0c;但是防止瀏覽器訪問時出現異常干擾日志&#xff0c;所…

oracle數據庫解析過高分析

解析非常高&#xff0c;通過時間模型可以看到解析占比非常高 解析大致可以分為硬解析&#xff08; hard parse&#xff09;、軟解析&#xff08; soft parse&#xff09;和軟軟解析&#xff08; soft soft parse&#xff09;。如&#xff0c;執行一條 SQL 的時候&#xff0c;如…

星型模型和雪花模型的區別

星型模型和雪花模型都是數據倉庫設計中常用的維度建模方法&#xff0c;它們之間的主要區別在于數據組織結構、數據冗余度、查詢效率、可擴展性和正規化程度等方面&#xff1a; 星型模型 結構特點&#xff1a;星型模型中&#xff0c;一個中心的事實表連接多個維度表&#xff0…

在電腦中,Apple Mobile Device Support程序是什么?

Apple Mobile Device Support 是一項關鍵的后臺服務&#xff0c;它為 Windows 用戶提供了與蘋果 iOS 設備&#xff08;包括 iPhone、iPad 和 iPod&#xff09;順暢互動的能力。這項服務的核心功能是為iTunes軟件提供必要的支持&#xff0c;使得用戶能夠將他們的移動設備與電腦連…

Google Find My Device:科技守護,安心無憂

在數字化的時代&#xff0c;我們的生活與各種智能設備緊密相連。而 Google Find My Device 便是一款為我們提供安心保障的實用工具。 一、Find My Decice Netword的定義 谷歌的Find My Device Netword旨在通過利用Android設備的眾包網絡的力量&#xff0c;幫助用戶安全的定位所…

HTML5 歷史、地理位置處理、全屏處理

目錄 歷史HistoryAPI地理位置處理GeolocationAPI全屏處理FullscreenAPIHistoryAPI window.history 對象 window.history 是瀏覽器提供的一個內置對象,它提供了對瀏覽器歷史記錄的訪問和操作能力。通過這個對象,開發者可以實現無刷新頁面跳轉、添加新的瀏覽歷史條目等,從而提…

STM32F4_HAL控制GPIO輸出——跑馬燈實驗

1、GPIO工作模式 1.1 端口輸入數據寄存器&#xff08;IDR&#xff09; 1.2 端口輸出數據寄存器&#xff08;ODR&#xff09; 1.3 端口置位/復位寄存器&#xff08;BSRR&#xff09; 為什么有了 ODR 寄存器&#xff0c;還要這個 BSRR 寄存器呢&#xff1f;我們先看看 BSRR 的寄…

23種設計模式(持續輸出中)

一.設計模式的作用 設計模式是軟件從業人員長期總結出來用于解決特定問題的通用性框架&#xff0c;它提高了代碼的可維護性、可擴展性、可讀性以及復用性。 二.設計模式 1.工廠模式 工廠模式提供了創建對象的接口&#xff0c;而無需制定創建對象的具體類&#xff0c;工廠類…

hive表在HDFS的每個文件的大小加大

配置參數&#xff1a; spark.hadoop.hive.exec.orc.default.stripe.size78643200 spark.hadoop.orc.stripe.size78643200 spark.hadoopRDD.targetBytesInPartition78643200 spark.hadoop.hive.exec.dynamic.partition.modenonstrict spark.sql.sources.partitionOverwriteMode…

華為OD機試 - 剩余銀飾的重量(Java 2024 C卷 100分)

華為OD機試 2024C卷題庫瘋狂收錄中&#xff0c;刷題點這里 專欄導讀 本專欄收錄于《華為OD機試&#xff08;JAVA&#xff09;真題&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一題都有詳細的答題思路、詳細的代碼注釋、樣例測試…

【Python】 如何對對象列表進行排序,有點意思

在Python中&#xff0c;我們經常需要對對象列表進行排序&#xff0c;這可以通過多種方式實現。當對象是一個自定義類實例時&#xff0c;排序通常基于對象的一個或多個屬性。Python提供了內置的sorted()函數和列表的sort()方法&#xff0c;它們都允許我們指定一個排序的關鍵字。…

iPhone徹底刪除的照片怎么恢復?專業技巧助您解憂

在使用iPhone的過程中&#xff0c;我們可能會因為誤操作或其他原因將一些重要的照片徹底刪除。然而&#xff0c;即使照片被徹底刪除&#xff0c;也并不意味著它們就永遠消失了&#xff0c;它們只是被打上了“可覆蓋的空間”的標簽。 在照片被新數據覆蓋之前&#xff0c;我們仍…

C語言學習【C控制語句:循環】

C語言學習【C控制語句&#xff1a;循環】 while循環 /* 根據用戶鍵入的整數求和 */#include "stdio.h"int main(void) {long num;long sum 0L; /* 把sum初始化為0 */int status;printf("Please enter an integer to be summed");printf(&quo…