深圳网站建设saote,建筑设计公司注册,手机wap网站如何建设,中国机械加工网19易5下2拉i【STM32】HAL库的STOP低功耗模式UART串口唤醒#xff0c;解决首字节出错的问题#xff08;全网第一解决方案#xff09;
前文#xff1a; 【STM32】HAL库的STOP低功耗模式UART串口唤醒#xff0c;第一个接收字节出错的问题#xff08;疑难杂症#xff09;
目前已解决 …【STM32】HAL库的STOP低功耗模式UART串口唤醒解决首字节出错的问题全网第一解决方案
前文 【STM32】HAL库的STOP低功耗模式UART串口唤醒第一个接收字节出错的问题疑难杂症
目前已解决 并更新了我的gitee库 基于HAL库建立自己的低功耗模式配置库STM32L4系列低功耗所有配置汇总 文章目录 最初的串口唤醒配置首字节出错的问题解决后的流程及代码为什么说是“全网第一”附录Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作SysTick系统定时器精准延时延时函数阻塞延时非阻塞延时 位带操作位带代码位带宏定义总线函数 一、位带操作理论及实践二、如何判断MCU的外设是否支持位带 最初的串口唤醒配置
在最初的串口唤醒配置中 我是用的如下配置现已更新
【STM32】HAL库低功耗STOP停止模式的串口唤醒解决进入以后立马唤醒、串口唤醒和回调无法一起使用、接收数据不全、首字节错误的问题
进入和退出函数为
/*!* brief 配置串口在停止模式下的唤醒 ** param [in] huart: UART_HandleTypeDef类型的器件* [in] EnableNotDisable: 使能或者关闭** return None*/
void Ctrl_UART_StopMode_WakeUp(UART_HandleTypeDef *huart,bool EnableNotDisable)
{ if(EnableNotDisable){__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI); //保留唤醒用的HSI线 串口初始化时钟也必须要配置为HSIUART_WakeUpTypeDef UART_WakeUpStruct{0};UART_WakeUpStruct.WakeUpEventUART_WAKEUP_ON_READDATA_NONEMPTY; //接收数据不为空时唤醒HAL_UARTEx_StopModeWakeUpSourceConfig(huart,UART_WakeUpStruct);__HAL_UART_ENABLE_IT(huart,UART_IT_WUF); //开启唤醒中断HAL_UARTEx_EnableStopMode(huart); //开启模式}else{__HAL_UART_DISABLE_IT(huart,UART_IT_WUF); //关闭唤醒中断HAL_UARTEx_DisableStopMode(huart); //关闭模式}
}
在唤醒后 我将时钟初始化等 放到了唤醒回调中 导致长数据中间丢数据 于是进行改进 就是把初始化等退出以后的函数放到主线程 低功耗函数之后 就能完美解决这个问题
在进入低功耗模式之前的流程为
保留HSI线系统RCC中的宏函数其他用到HSI时钟唤醒的外设也建议进行此步骤这里串口也必须要用这条时钟线配置串口唤醒方式开启串口唤醒中断可以不开开启串口唤醒功能
其中 开启串口唤醒功能就是将此寄存器置1
当然 这里的串口唤醒是抄的网上的代码和流程 教程很多 我就不叙述了 不过最后我发现在高速UART波特率的情况下 首唤醒字节仍然会丢失 且无论怎么改 都会出现此问题 具体调试和BUG复现过程 【STM32】HAL库的STOP低功耗模式UART串口唤醒第一个接收字节出错的问题疑难杂症
首字节出错的问题
经过前文的调试 首字节在出错时 是将采样后移了一位 发送AA 接收到55 也就是发送1010 1010 接收到0101 0101 所以我猜想应该是时钟的问题
进入低功耗需要时间 同样 唤醒也需要时间 所以可能就是这么点时间导致CPU延迟采样 所以串口接收不准 同样 在进入低功耗后 可以看到电流也是缓慢下降的 而采用jlink烧录后 实际上功耗下不来大个1ma左右 推测应该是jlink的信号复位保持了某些寄存器不清零毕竟jlink和复位也算中断的一种
这个问题困扰我好几天 我心中知道一定是时钟的问题 但一直没办法解决 直到我开发STM32L4R系列时 发现一个寄存器如L496不同 也就是CR3的第23位 起初 我以为是芯片手册版本的问题 直到我查看SDK发现真的如此 有个操作函数HAL_UARTEx_EnableClockStopMode长这样
#if defined(USART_CR3_UCESM)
/*** brief Keep UART Clock enabled when in Stop Mode.* note When the USART clock source is configured to be LSE or HSI, it is possible to keep enabled* this clock during STOP mode by setting the UCESM bit in USART_CR3 control register.* note When LPUART is used to wakeup from stop with LSE is selected as LPUART clock source,* and desired baud rate is 9600 baud, the bit UCESM bit in LPUART_CR3 control register must be set.* param huart UART handle.* retval HAL status*/
HAL_StatusTypeDef HAL_UARTEx_EnableClockStopMode(UART_HandleTypeDef *huart)
{/* Process Locked */__HAL_LOCK(huart);/* Set UCESM bit */SET_BIT(huart-Instance-CR3, USART_CR3_UCESM);/* Process Unlocked */__HAL_UNLOCK(huart);return HAL_OK;
}
而部分芯片是没有USART_CR3_UCESM这一位的
#define USART_CR3_UCESM_Pos (23U)
#define USART_CR3_UCESM_Msk (0x1UL USART_CR3_UCESM_Pos) /*! 0x02000000 */
#define USART_CR3_UCESM USART_CR3_UCESM_Msk /*! USART Clock enable in Stop mode */这个函数位于uart_ex中 注释里面说明了 要保证在低功耗时 保持时钟 所以要用到的这个函数 另外 采用LPUART时 如果波特率在9600之上 这一位需要置1
而函数HAL_UARTEx_EnableStopMode只对USART_CR1_UESM进行了操作 此寄存器是通用的 并且SDK中的函数也是通用的
/*** brief Enable UART Stop Mode.* note The UART is able to wake up the MCU from Stop 1 mode as long as UART clock is HSI or LSE.* param huart UART handle.* retval HAL status*/
HAL_StatusTypeDef HAL_UARTEx_EnableStopMode(UART_HandleTypeDef *huart)
{/* Process Locked */__HAL_LOCK(huart);/* Set UESM bit */SET_BIT(huart-Instance-CR1, USART_CR1_UESM);/* Process Unlocked */__HAL_UNLOCK(huart);return HAL_OK;
}所以 我就把函数HAL_UARTEx_EnableClockStopMode添加到了进入低功耗之前 然后就解决了BUG 停止模式下的BUG再也没出错
解决后的流程及代码
最后的串口唤醒配置流程就改为
保留HSI线系统RCC中的宏函数其他用到HSI时钟唤醒的外设也建议进行此步骤这里串口也必须要用这条时钟线配置串口唤醒方式开启串口唤醒中断可以不开开启串口STOP模式时钟 开启串口唤醒功能
代码
/*!* brief 配置串口在停止模式下的唤醒 ** param [in] huart: UART_HandleTypeDef类型的器件* [in] EnableNotDisable: 使能或者关闭** return None*/
void Ctrl_UART_StopMode_WakeUp(UART_HandleTypeDef *huart,bool EnableNotDisable)
{ if(EnableNotDisable){__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI); //保留唤醒用的HSI线 串口初始化时钟也必须要配置为HSIUART_WakeUpTypeDef UART_WakeUpStruct{0};UART_WakeUpStruct.WakeUpEventUART_WAKEUP_ON_READDATA_NONEMPTY; //接收数据不为空时唤醒HAL_UARTEx_StopModeWakeUpSourceConfig(huart,UART_WakeUpStruct);__HAL_UART_ENABLE_IT(huart,UART_IT_WUF); //开启唤醒中断HAL_UARTEx_EnableClockStopMode(huart);HAL_UARTEx_EnableStopMode(huart); //开启模式}else{__HAL_UART_DISABLE_IT(huart,UART_IT_WUF); //关闭唤醒中断HAL_UARTEx_DisableClockStopMode(huart);HAL_UARTEx_DisableStopMode(huart); //关闭模式}
}
其中 第一步的保留HSI时钟线 也就是RCC中的宏函数 这一步我在测试后发现 如果使用了HAL_UARTEx_EnableClockStopMode函数 则不需要进行第一步 但是串口所使用的时钟还是要配置为HSI线
为什么说是“全网第一”
最后 我在网上搜素HAL_UARTEx_EnableClockStopMode函数 发现国内无论是CSDN还是百度都搜不到
只有GitHub上寥寥无几的几个搜素结果 并且都是与LPUART有关的虽然这个与UART配置流程和函数基本一样
附录Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作
SysTick系统定时器精准延时
延时函数
SysTick-LOAD中的值为计数值 计算方法为工作频率值/分频值 比如工作频率/1000 则周期为1ms
以ADuCM4050为例
#include ADuCM4050.hvoid 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)(bitnum2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))#define GPIO0_ODR_Addr (ADI_GPIO0_BASE20) //0x40020014
#define GPIO0_IDR_Addr (ADI_GPIO0_BASE16) //0x40020010#define GPIO1_ODR_Addr (ADI_GPIO1_BASE20) //0x40020054
#define GPIO1_IDR_Addr (ADI_GPIO1_BASE16) //0x40020050#define GPIO2_ODR_Addr (ADI_GPIO2_BASE20) //0x40020094
#define GPIO2_IDR_Addr (ADI_GPIO2_BASE16) //0x40020090#define GPIO3_ODR_Addr (ADI_GPIO3_BASE20) //0x400200D4
#define GPIO3_IDR_Addr (ADI_GPIO3_BASE16) //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.hvoid GPIO_OUT(unsigned int port,unsigned int pin,unsigned int flag)
{switch(port){case 0:{switch(pin){case 0:if(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_15));};break;default:pin0;break;}}break;case 1:{switch(pin){case 0:if(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_15));};break;default:pin0;break;}}break;case 2:{switch(pin){case 0:if(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_15));};break;default:pin0;break;}}break;case 3:{switch(pin){case 0:if(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_15));};break;default:pin0;break;}}break;default:port0;break;}
}void GPIO_BUS_OUT(unsigned int port,unsigned int num) //num最大为0xffff
{int i;for(i0;i16;i){GPIO_OUT(port,i,(numi)0x0001);}
}void P0_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){P0_O(i)(numi)0x0001;}
}
unsigned int P0_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(P0_I(i)i)0xFFFF;}return num;
}void P1_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){P1_O(i)(numi)0x0001;}
}
unsigned int P1_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(P1_I(i)i)0xFFFF;}return num;
}void P2_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){P2_O(i)(numi)0x0001;}
}
unsigned int P2_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(P2_I(i)i)0xFFFF;}return num;
}void P3_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){P3_O(i)(numi)0x0001;}
}
unsigned int P3_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(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)(bitnum2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))那么 设置PF引脚的函数可以定义
#define GPIOF_ODR_Addr (GPIOF_BASE20) //0x40021414
#define GPIOF_IDR_Addr (GPIOF_BASE16) //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(i0;i16;i){PF_O(i)(numi)0x0001;}
}
unsigned int PF_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(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)(bitnum2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))#define GPIOA_ODR_Addr (GPIOA_BASE20) //0x40020014
#define GPIOB_ODR_Addr (GPIOB_BASE20) //0x40020414
#define GPIOC_ODR_Addr (GPIOC_BASE20) //0x40020814
#define GPIOD_ODR_Addr (GPIOD_BASE20) //0x40020C14
#define GPIOE_ODR_Addr (GPIOE_BASE20) //0x40021014
#define GPIOF_ODR_Addr (GPIOF_BASE20) //0x40021414
#define GPIOG_ODR_Addr (GPIOG_BASE20) //0x40021814
#define GPIOH_ODR_Addr (GPIOH_BASE20) //0x40021C14
#define GPIOI_ODR_Addr (GPIOI_BASE20) //0x40022014 #define GPIOA_IDR_Addr (GPIOA_BASE16) //0x40020010
#define GPIOB_IDR_Addr (GPIOB_BASE16) //0x40020410
#define GPIOC_IDR_Addr (GPIOC_BASE16) //0x40020810
#define GPIOD_IDR_Addr (GPIOD_BASE16) //0x40020C10
#define GPIOE_IDR_Addr (GPIOE_BASE16) //0x40021010
#define GPIOF_IDR_Addr (GPIOF_BASE16) //0x40021410
#define GPIOG_IDR_Addr (GPIOG_BASE16) //0x40021810
#define GPIOH_IDR_Addr (GPIOH_BASE16) //0x40021C10
#define GPIOI_IDR_Addr (GPIOI_BASE16) //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.hvoid PA_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){PA_O(i)(numi)0x0001;}
}
unsigned int PA_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(PA_I(i)i)0xFFFF;}return num;
}void PB_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){PB_O(i)(numi)0x0001;}
}
unsigned int PB_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(PB_I(i)i)0xFFFF;}return num;
}void PC_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){PC_O(i)(numi)0x0001;}
}
unsigned int PC_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(PC_I(i)i)0xFFFF;}return num;
}void PD_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){PD_O(i)(numi)0x0001;}
}
unsigned int PD_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(PD_I(i)i)0xFFFF;}return num;
}void PE_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){PE_O(i)(numi)0x0001;}
}
unsigned int PE_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(PE_I(i)i)0xFFFF;}return num;
}void PF_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){PF_O(i)(numi)0x0001;}
}
unsigned int PF_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(PF_I(i)i)0xFFFF;}return num;
}void PG_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){PG_O(i)(numi)0x0001;}
}
unsigned int PG_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(PG_I(i)i)0xFFFF;}return num;
}void PH_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){PH_O(i)(numi)0x0001;}
}
unsigned int PH_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(PH_I(i)i)0xFFFF;}return num;
}void PI_BUS_O(unsigned int num) //输入值num最大为0xFFFF
{int i;for(i0;i16;i){PI_O(i)(numi)0x0001;}
}
unsigned int PI_BUS_I(void) //输出值num最大为0xFFFF
{unsigned int num;int i;for(i0;i16;i){numnum(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