【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é)接收有問題的情況
- 進(jìn)入STOP1模式
- 配置串口喚醒
- 使用jlink復(fù)位以后斷電并重新上電
- 每次進(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) :
第一個字節(jié)發(fā)68 實(shí)際接收的是B4
第一個字節(jié)發(fā)AA 實(shí)際接收是55
那么就可以看到 實(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ù)位也算中斷的一種)
如果想徹底解決 可以做此嘗試:
- 串口發(fā)送時預(yù)留一位或多位
- 降低串口波特率
- 改用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è)。
(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)行
要設(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)
但好在官方庫里面都幫我們定義好了 只需要在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é)描述
也就是說 要實(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 就可以用位帶來更改文章來源:http://www.zghlxwxcb.cn/news/detail-791797.html
STM32L476的GPIO就不行:
AHB2的都不能用位帶
ABP 還有AHB1都可以用
但是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)!