国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決)

這篇具有很好參考價值的文章主要介紹了【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決)

BUG復(fù)現(xiàn)

最近做項(xiàng)目時 用到了STOP1停止模式的串口喚醒 喚醒配置如下:

【STM32】HAL庫低功耗STOP停止模式的串口喚醒(解決進(jìn)入以后立馬喚醒、串口喚醒和回調(diào)無法一起使用、接收數(shù)據(jù)不全的問題)

我配置的是串口波特率115200 1停止位 Even偶校驗(yàn)
發(fā)現(xiàn)偶然會出現(xiàn)喚醒以后第一個字節(jié)接收有問題的情況

  1. 進(jìn)入STOP1模式
  2. 配置串口喚醒
  3. 使用jlink復(fù)位以后斷電并重新上電
  4. 每次進(jìn)入停止模式2s后發(fā)送串口喚醒 就能復(fù)現(xiàn)該BUG 并且穩(wěn)定出現(xiàn)

而在每次燒錄代碼并用jlink復(fù)位后 完全不會出現(xiàn)此BUG的情況

調(diào)試代碼

經(jīng)過調(diào)試發(fā)現(xiàn) :
【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決),STM32筆記(含疑難雜癥),stm32,bug,單片機(jī),物聯(lián)網(wǎng),mcu,嵌入式,iot
第一個字節(jié)發(fā)68 實(shí)際接收的是B4
第一個字節(jié)發(fā)AA 實(shí)際接收是55
【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決),STM32筆記(含疑難雜癥),stm32,bug,單片機(jī),物聯(lián)網(wǎng),mcu,嵌入式,iot

那么就可以看到 實(shí)際上是串口接收回調(diào)在進(jìn)行接收采樣時 往后移了一位 所以數(shù)據(jù)錯了

推測原因及改進(jìn)方案嘗試

中斷

串口接收中斷是發(fā)生在喚醒中斷之前的
后移一位數(shù)據(jù)可能是喚醒中斷導(dǎo)致 所以我關(guān)了中斷
將以下注釋:

//__HAL_UART_ENABLE_IT(huart,UART_IT_WUF);	//開啟喚醒中斷

無法解決

時鐘

配置串口喚醒需要保留HSI 喚醒后重新對整體時鐘進(jìn)行初始化
將喚醒后的時鐘配置注釋 無法解決

//SystemClock_Config();	

供電

由于jlink燒錄后沒問題 再次上電復(fù)位就有問題 所以推測是供電不穩(wěn)定導(dǎo)致
【STM32】HAL庫的RCC復(fù)位狀態(tài)判斷及NVIC系統(tǒng)軟件復(fù)位
在初始化前加了個判斷復(fù)位的函數(shù) 并使用軟件復(fù)位一次

if (__HAL_RCC_GET_FLAG(RCC_FLAG_BORRST) != RESET)
{
	__HAL_RCC_CLEAR_RESET_FLAGS();
	printf("[INFO] 設(shè)備冷啟動 0.5s后軟件復(fù)位\n");
	delay_ms(500);
	HAL_NVIC_SystemReset();
}	

無法解決

外設(shè)

由于喚醒前會DeInit其他外設(shè) 喚醒后重新初始化 推測是不是GPIO或者其他外設(shè)初始化導(dǎo)致
注釋掉相關(guān)函數(shù)
無法解決

喚醒方式

串口喚醒有三種 分別是接收到幀 接收到數(shù)據(jù) 接收到起始位
用這三種都不行

校驗(yàn)碼

改成無校驗(yàn)碼 無法解決

硬件問題

更換了其他板子和串口模塊 無法解決

切換到STOP0模式嘗試

STOP0最初效果比STOP1好很多 重新上電復(fù)位等待2s也能喚醒 并且第一個數(shù)據(jù)不出錯
但是一直低功耗很長時間以后 再次喚醒則又會出錯
并且 當(dāng)我配置為起始位喚醒時 隔了一段時間以后 串口雖然可以喚醒 但接收的那一條數(shù)據(jù)全為0
改回接收到幀喚醒后 與STOP1的BUG相同

結(jié)論和猜想

進(jìn)入低功耗需要時間 同樣 喚醒也需要時間 所以可能就是這么點(diǎn)時間導(dǎo)致CPU延遲采樣 所以串口接收不準(zhǔn)
同樣 在進(jìn)入低功耗后 可以看到電流也是緩慢下降的 而采用jlink燒錄后 實(shí)際上功耗下不來(大個1ma左右) 推測應(yīng)該是jlink的信號復(fù)位保持了某些寄存器不清零(畢竟jlink和復(fù)位也算中斷的一種)

如果想徹底解決 可以做此嘗試:

  1. 串口發(fā)送時預(yù)留一位或多位
  2. 降低串口波特率
  3. 改用sleep模式或其他方案(sleep模式喚醒無延遲)

解決方案

增加一個函數(shù)HAL_UARTEx_EnableClockStopMode__HAL_RCC_HSISTOP_ENABLE()(我推薦前者)即可
具體參考下文:
【STM32】HAL庫的STOP低功耗模式UART串口喚醒,解決首字節(jié)出錯的問題(全網(wǎng)第一解決方案)

附錄:Cortex-M架構(gòu)的SysTick系統(tǒng)定時器精準(zhǔn)延時和MCU位帶操作

SysTick系統(tǒng)定時器精準(zhǔn)延時

延時函數(shù)

SysTick->LOAD中的值為計數(shù)值
計算方法為工作頻率值/分頻值
比如工作頻率/1000 則周期為1ms

以ADuCM4050為例:

#include "ADuCM4050.h"

void delay_ms(unsigned int ms)
{
	SysTick->LOAD = 26000000/1000-1; // Count from 255 to 0 (256 cycles)  載入計數(shù)值 定時器從這個值開始計數(shù)
	SysTick->VAL = 0; // Clear current value as well as count flag  清空計數(shù)值到達(dá)0后的標(biāo)記
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能52MHz的系統(tǒng)定時器
	while(ms--)
	{
		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
	}
	SysTick->CTRL = 0; // Disable SysTick  關(guān)閉系統(tǒng)定時器
}
void delay_us(unsigned int us)
{
	SysTick->LOAD = 26000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  載入計數(shù)值 定時器從這個值開始計數(shù)
	SysTick->VAL = 0; // Clear current value as well as count flag  清空計數(shù)值到達(dá)0后的標(biāo)記
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能52MHz的系統(tǒng)定時器
	while(us--)
	{
		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
	}
	SysTick->CTRL = 0; // Disable SysTick  關(guān)閉系統(tǒng)定時器
}

其中的52000000表示芯片的系統(tǒng)定時器頻率 32系列一般為外部定時器頻率的兩倍

Cortex-M架構(gòu)SysTick系統(tǒng)定時器阻塞和非阻塞延時

阻塞延時

首先是最常用的阻塞延時

void delay_ms(unsigned int ms)
{
	SysTick->LOAD = 50000000/1000-1; // Count from 255 to 0 (256 cycles)  載入計數(shù)值 定時器從這個值開始計數(shù)
	SysTick->VAL = 0; // Clear current value as well as count flag  清空計數(shù)值到達(dá)0后的標(biāo)記
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系統(tǒng)定時器
	while(ms--)
	{
		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
	}
	SysTick->CTRL = 0; // Disable SysTick  關(guān)閉系統(tǒng)定時器
}
void delay_us(unsigned int us)
{
	SysTick->LOAD = 50000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  載入計數(shù)值 定時器從這個值開始計數(shù)
	SysTick->VAL = 0; // Clear current value as well as count flag  清空計數(shù)值到達(dá)0后的標(biāo)記
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系統(tǒng)定時器
	while(us--)
	{
		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
	}
	SysTick->CTRL = 0; // Disable SysTick  關(guān)閉系統(tǒng)定時器
}

50000000表示工作頻率
分頻后即可得到不同的延時時間
以此類推

那么 不用兩個嵌套while循環(huán) 也可以寫成:

void delay_ms(unsigned int ms)
{
	SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  載入計數(shù)值 定時器從這個值開始計數(shù)
	SysTick->VAL = 0; // Clear current value as well as count flag  清空計數(shù)值到達(dá)0后的標(biāo)記
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系統(tǒng)定時器

	while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待

	SysTick->CTRL = 0; // Disable SysTick  關(guān)閉系統(tǒng)定時器
}
void delay_us(unsigned int us)
{
	SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  載入計數(shù)值 定時器從這個值開始計數(shù)
	SysTick->VAL = 0; // Clear current value as well as count flag  清空計數(shù)值到達(dá)0后的標(biāo)記
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系統(tǒng)定時器
	
	while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待

	SysTick->CTRL = 0; // Disable SysTick  關(guān)閉系統(tǒng)定時器
}

但是這種寫法有個弊端
那就是輸入ms后,最大定時不得超過計數(shù)值,也就是不能超過LOAD的最大值,否則溢出以后,則無法正常工作

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

晶振為50M的話 50M的計數(shù)值為1s 4294967295計數(shù)值約為85s

固最大定時時間為85s

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

非阻塞延時

如果采用非阻塞的話 直接改寫第二種方法就好了:

void delay_ms(unsigned int ms)
{
	SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  載入計數(shù)值 定時器從這個值開始計數(shù)
	SysTick->VAL = 0; // Clear current value as well as count flag  清空計數(shù)值到達(dá)0后的標(biāo)記
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系統(tǒng)定時器

	//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待

	//SysTick->CTRL = 0; // Disable SysTick  關(guān)閉系統(tǒng)定時器
}
void delay_us(unsigned int us)
{
	SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  載入計數(shù)值 定時器從這個值開始計數(shù)
	SysTick->VAL = 0; // Clear current value as well as count flag  清空計數(shù)值到達(dá)0后的標(biāo)記
	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系統(tǒng)定時器
	
	//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待

	//SysTick->CTRL = 0; // Disable SysTick  關(guān)閉系統(tǒng)定時器
}

將等待和關(guān)閉定時器語句去掉
在使用時加上判斷即可變?yōu)樽枞?/p>

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

在非阻塞狀態(tài)下 可以提交定時器后 去做別的事情 然后再來等待

不過這樣又有一個弊端 那就是定時器會自動重載 可能做別的事情以后 定時器跑過了 然后就要等85s才能停下

故可以通過內(nèi)部定時器來進(jìn)行非阻塞延時函數(shù)的編寫

基本上每個mcu的內(nèi)部定時器都可以配置自動重載等功能 網(wǎng)上資料很多 這里就不再闡述了

位帶操作

位帶代碼

M3、M4架構(gòu)的單片機(jī) 其輸出口地址為端口地址+20 輸入為+16
M0架構(gòu)的單片機(jī) 其輸出口地址為端口地址+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

總線函數(shù)
#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;
}

一、位帶操作理論及實(shí)踐

位帶操作的概念其實(shí)30年前就有了,那還是 CM3 將此能力進(jìn)化,這里的位帶操作是 8051 位尋址區(qū)的威力大幅加強(qiáng)版

位帶區(qū): 支持位帶操作的地址區(qū)

位帶別名: 對別名地址的訪問最終作 用到位帶區(qū)的訪問上(注意:這中途有一個 地址映射過程)

位帶操作對于硬件 I/O 密集型的底層程序最有用處

支持了位帶操作后,可以使用普通的加載/存儲指令來對單一的比特進(jìn)行讀寫。在CM4中,有兩個區(qū)中實(shí)現(xiàn)了位帶。其中一個是SRAM區(qū)的最低1MB范圍,第二個則是片內(nèi)外設(shè)區(qū)的最低1MB范圍。這兩個區(qū)中的地址除了可以像普通的RAM一樣使用外,它們還都有自己的“位帶別名區(qū)”,位帶別名區(qū)把每個比特膨脹成一個32位的字。當(dāng)你通過位帶別名區(qū)訪問這些字時,就可以達(dá)到訪問原始比特的目的。

位操作就是可以單獨(dú)的對一個比特位讀和寫,類似與51中sbit定義的變量,stm32中通過訪問位帶別名區(qū)來實(shí)現(xiàn)位操作的功能
STM32中有兩個地方實(shí)現(xiàn)了位帶,一個是SRAM,一個是片上外設(shè)。
【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決),STM32筆記(含疑難雜癥),stm32,bug,單片機(jī),物聯(lián)網(wǎng),mcu,嵌入式,iot
(1)位帶本質(zhì)上是一塊地址區(qū)(例如每一位地址位對應(yīng)一個寄存器)映射到另一片地址區(qū)(實(shí)現(xiàn)每一位地址位對應(yīng)一個寄存器中的一位),該區(qū)域就叫做位帶別名區(qū),將每一位膨脹成一個32位的字。
(2)位帶區(qū)的4個字節(jié)對應(yīng)實(shí)際寄存器或內(nèi)存區(qū)的一個位,雖然變大到4個字節(jié),但實(shí)際上只有最低位有效(代表0或1)

只有位帶可以直接用=賦值的方式來操作寄存器 位帶是把寄存器上的每一位 膨脹到32位 映射到位帶區(qū) 比如0x4002 0000地址的第0個bit 映射到位帶區(qū)的0地址 那么其對應(yīng)的位帶映射地址為0x00 - 0x04 一共32位 但只有LSB有效 采用位帶的方式用=賦值時 就是把位帶區(qū)對應(yīng)的LSB賦值 然后MCU再轉(zhuǎn)到寄存器對應(yīng)的位里面 寄存器操作時 如果不改變其他位上面的值 那就只能通過&=或者|=的方式進(jìn)行

【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決),STM32筆記(含疑難雜癥),stm32,bug,單片機(jī),物聯(lián)網(wǎng),mcu,嵌入式,iot

要設(shè)置0x2000 0000這個字節(jié)的第二個位bit2為1,使用位帶操作的步驟有:
1、將1寫入位 帶別名區(qū)對應(yīng)的映射地址(即0x22000008,因?yàn)?bit對應(yīng)4個byte);
2、將0x2000 0000的值 讀取到內(nèi)部的緩沖區(qū)(這一步驟是內(nèi)核完成的,屬于原子操作,不需要用戶操作);
3、將bit2置1,再把值寫 回到0x2000 0000(屬于原子操作,不需要用戶操作)。

關(guān)于GPIO引腳對應(yīng)的訪問地址,可以參考以下公式
寄存器位帶別名 = 0x42000000 + (寄存器的地址-0x40000000)32 + 引腳編號4

如:端口F訪問的起始地址GPIOF_BASE

#define GPIOF ((GPIO_TypeDef *)GPIOF_BASE)

【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決),STM32筆記(含疑難雜癥),stm32,bug,單片機(jī),物聯(lián)網(wǎng),mcu,嵌入式,iot

但好在官方庫里面都幫我們定義好了 只需要在BASE地址加上便宜即可

例如:

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

寄存器位帶別名 = 0x42000000 + (寄存器的地址-0x40000000)32 + 引腳編號4

設(shè)置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)

現(xiàn)在 可以把通用部分封裝成一個小定義:

#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))

那么 設(shè)置PF引腳的函數(shù)可以定義:

#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的可用下面的函數(shù):

#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的外設(shè)是否支持位帶

根據(jù)《ARM Cortex-M3與Cortex-M4權(quán)威指南(第3版)》中第6章第7節(jié)描述
【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決),STM32筆記(含疑難雜癥),stm32,bug,單片機(jī),物聯(lián)網(wǎng),mcu,嵌入式,iot
也就是說 要實(shí)現(xiàn)對GPIO的位帶操作 必須保證GPIO位于外設(shè)區(qū)域的第一個1MB中
第一個1MB應(yīng)該是0x4010 0000之前 位帶不是直接操作地址 而是操作地址映射 地址映射被操作以后 MCU自動會修改對應(yīng)寄存器的值

位帶區(qū)只有1MB 所以只能改0x4000 0000 - 0x400F FFFF的寄存器
像F4系列 GPIO的首地址為0x4002 0000 就可以用位帶來更改

STM32L476的GPIO就不行:
【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決),STM32筆記(含疑難雜癥),stm32,bug,單片機(jī),物聯(lián)網(wǎng),mcu,嵌入式,iot
AHB2的都不能用位帶
ABP 還有AHB1都可以用
【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決),STM32筆記(含疑難雜癥),stm32,bug,單片機(jī),物聯(lián)網(wǎng),mcu,嵌入式,iot
但是L476的寄存器里面 GPIO和ADC都是AHB2文章來源地址http://www.zghlxwxcb.cn/news/detail-791797.html

到了這里,關(guān)于【STM32】HAL庫的STOP低功耗模式UART串口喚醒,第一個接收字節(jié)出錯的問題(已解決)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • STM32 HAL庫的HAL_UART_Transmit_IT使用方法

    STM32 HAL庫的HAL_UART_Transmit_IT使用方法

    是STM32 HAL庫中非阻塞的串口發(fā)送函數(shù)。 用法:1. 調(diào)用HAL_UART_Transmit_IT()發(fā)送數(shù)據(jù) ? ? ? ? ? ?2. 在HAL_UART_TxCpltCallback()里寫上發(fā)送完成后的處理 注意:?HAL_UART_Transmit_IT()要等待上次發(fā)送完成后再發(fā)送,否則返回HAL_BUSY。用huart-gState == HAL_UART_STATE_READY判斷上次是否發(fā)送完成。 官方

    2024年02月16日
    瀏覽(24)
  • APM32F072單片機(jī)進(jìn)入STOP模式,并通過RTC Wakeup Timer和USART1串口接收事件喚醒

    串口初始化(注意USART1時鐘源要選擇HSI): 使用power_init函數(shù)初始化RTC,然后調(diào)用power_enter_stop_mode(n)函數(shù)進(jìn)入STOP模式,n秒后自動喚醒,或由USART1接收喚醒:

    2024年02月13日
    瀏覽(24)
  • 【STM32筆記】低功耗模式下的RTC喚醒(非鬧鐘喚醒,而是采用RTC_WAKEUPTIMER)

    【STM32筆記】低功耗模式下的RTC喚醒(非鬧鐘喚醒,而是采用RTC_WAKEUPTIMER)

    【STM32】低功耗模式下的RTC喚醒(非鬧鐘喚醒,而是采用RTC_WAKEUPTIMER) 【STM32筆記】低功耗模式配置及避坑匯總 前文: blog.csdn.net/weixin_53403301/article/details/128216064 【STM32筆記】HAL庫低功耗模式配置(ADC喚醒無法使用、低功耗模式無法燒錄解決方案) 低功耗模式如圖所示 停止模

    2023年04月08日
    瀏覽(19)
  • STM32 HAL庫 STM32CubeMx -- 串口的使用(USART/UART)

    STM32 HAL庫 STM32CubeMx -- 串口的使用(USART/UART)

    在上一篇博客里面寫了串口通信的理論知識,在這一篇中將講述串口通信在STM32CubeMx里面的配置,以及在函數(shù)里面怎么使用。 對于串口發(fā)送信息,分為三種方法: 串口阻塞方式收發(fā) 、 串口中斷方式收發(fā) 、 串口DMA方式收發(fā) 。(DMA方式在之后的DMA章節(jié)講解) 關(guān)于STM32CubeMx的基

    2024年02月06日
    瀏覽(26)
  • STM32CUBEMX 待機(jī)模式最簡單的RTC定時喚醒(低功耗電池產(chǎn)品必備)

    STM32CUBEMX 待機(jī)模式最簡單的RTC定時喚醒(低功耗電池產(chǎn)品必備)

    看到很多技術(shù)帖子講述RTC定時喚醒功能的時候,老是需要去讀取當(dāng)前時間,再設(shè)定下一個鬧鐘喚醒時間,無形中多了很多變量和操作。所以我決定分享一種簡單的RTC定時喚醒方法,適合于不需要實(shí)現(xiàn)具體時間獲取的場合,僅實(shí)現(xiàn)RTC定時喚醒的功能。 電池類便攜式產(chǎn)品一般都要

    2024年02月03日
    瀏覽(25)
  • STM32 HAL庫的串口中斷服務(wù)函數(shù)詳解

    STM32 HAL庫的串口中斷服務(wù)函數(shù)詳解

    最近在實(shí)現(xiàn)利用上位機(jī)通過串口發(fā)送指令給下位機(jī)執(zhí)行操作的實(shí)驗(yàn),在之前學(xué)習(xí)串口的過程中我就一直有一個疑惑,那就是為什么在串口中斷回調(diào)函數(shù)內(nèi)除了要加上自己的操作以外還要在末尾再執(zhí)行一次 接收中斷 ,在查閱了一些資料后我才發(fā)現(xiàn)原來和 中斷服務(wù)函數(shù) 有關(guān) 我

    2024年02月10日
    瀏覽(21)
  • STM32基于HAL庫的串口接受中斷和空閑中斷

    在通信方面。UART由于全雙工通信,可以同時接受數(shù)據(jù)和發(fā)送數(shù)據(jù)而被廣泛使用。 而接受數(shù)據(jù)則又有很多種方法 比如: 1根據(jù)結(jié)束符判斷,數(shù)據(jù)是字符串形式,所以一般串口接受的接受符就是 \\\"rn\\\"? 換成16進(jìn)制ascil碼顯示就是 0X0D? ?0X0A (對應(yīng)rn) ?2定時器中斷,設(shè)計\\\"喂狗信號量

    2023年04月08日
    瀏覽(22)
  • STM32 HAL 庫 串口 函數(shù)HAL_UART_Transmit的BUG問題 及解決方法

    STM32 HAL 庫 串口 函數(shù)HAL_UART_Transmit的BUG問題 及解決方法

    近期在開發(fā)圖傳項(xiàng)目的時候,由于需要發(fā)送的數(shù)據(jù)量及其龐大,因此在處理的時候, 發(fā)現(xiàn)STM32HAL庫的串口函數(shù),在處理海量數(shù)據(jù)的時候, 存在bug, 導(dǎo)致不能將指定數(shù)量的數(shù)據(jù)全部發(fā)送出去。? 例如, 我以200個字節(jié)為一個數(shù)據(jù)包, 使用HAL_UART_Transmit函數(shù),通過串口發(fā)送。 那么

    2024年02月13日
    瀏覽(27)
  • stm32中HAL_UART_Transmit_DMA,串口只能發(fā)送一次。

    stm32中HAL_UART_Transmit_DMA,串口只能發(fā)送一次。

    配置串口+DMA不產(chǎn)生DMA中斷方式, 1、HAL_UART_Transmit_DMA中調(diào)用HAL_DMA_Start_IT,在HAL_DMA_Start_IT中雖然開啟了三個中斷:hdma-Instance-CR ?|= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME,但是沒有開DMA總中斷,所以這3個不起作用;同時,鎖住DMA(?__HAL_LOCK(hdma))。如果開啟DMA總中斷,完成DMA后會調(diào)用下

    2024年04月28日
    瀏覽(32)
  • 【STM32】HAL庫UART含校驗(yàn)位的串口通信配置BUG避坑

    【STM32】HAL庫UART含校驗(yàn)位的串口通信配置BUG避坑

    【STM32】HAL庫UART含校驗(yàn)位的串口通信配置BUG避坑 UART通過一條線就能完成數(shù)據(jù)的發(fā)送 另外一條線則完成數(shù)據(jù)的接收 所以一共是兩條線 TX RX UART在空閑時為低電平 時鐘周期由波特率確定 通常是115200bit/s UART協(xié)議由四個部分組成: 起始位: 固定低電平 1個時鐘周期 數(shù)據(jù)域: 通常

    2024年01月20日
    瀏覽(32)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包