研究了好久,总算是成功实现了LPBAM的测试。感觉STM32U5的这几个新功能都有些难点。上个测试TrustZone我觉得就挺复杂的。
LPBAM是STM32U5低功耗的一个特色功能。能够工作在STOP2模式,在这个模式下,没有CPU的参与,只使用DMA控制器实现一定的功能,比如外设数据的采集、外设的配置。而且可以借助一些外设产生的事件触发操作。比如使用定时器的PWM上升沿触发IO的翻转,或者使用ADC的模拟看门狗的事件改变ADC的采样频率等功能。因为不需要使用CPU,所以能够大大的降低功耗。
LPBAM不使用CPU,而是使用DMA控制器。因此在使用的时候需要分配一个DMA通道。在这种模式下使用的外设也都是低功耗外设,比如LPDAM、LPGPIO等。
LPBAM也使用外设的事件,触发一定的操作,比如对外设数据的采集,将数据传输到SRAM,或者将SRAM的数据发送到外设;也可以对外设的寄存器进行配置,更改外设的参数,比如定时器的周期。
STM32CUBEMX软件也集成了LPBAM的配置功能。使用这个软件能够更快的完成LPBAM功能的设置。比如要实现如下功能:在STOP2低功耗模式下,使用LPTIM1产生1s周期的PWM,在上升沿的时候触发LPGPIO0实现翻转的功能。(翻转的功能是通过DMA向GPIO的BSRR寄存器写数据实现的)
需要配置的外设:
- 在普通模式下分配一个LPDMA的通道,使用linked-list模式。
- 初始化在LPBAM模式下使用LPTIM1、LPGPIO0
- 设置LPBAM下LPTIM1使用的时钟源
在STM32CUBEMX中的步骤
建立工程后点击上面的按键可以进入LPBAM的设置界面。
进入设置界面之后,需要首先建立一个LPBAM的应用。如下图所示。可以看出这是3级目录,第一级是应用级Application,不同的应用可以有不同的外设配置。应用下是场景scenario,场景好像只能是一个,不能新增。再下一级是队列queue,这个是具体的在LPBAM模式下的工作流程,这个工作流程是DMA控制器的工作流程。在增加具体的queue之前,先把在LPBAM里使用的外设初始化了。
在"Pinout & Configuration"标签里初始化外设。设置LPTIM1的CH1工作在PWM模式下,并配置周期为1s,占空比为50% 。
配置LPGPIO0(PA1)工作在输出模式。
然后在低功耗里配置,在STOP2模式下只使用SRAM4和ICACHE,这样可以降低功耗。
然后选择LPTIM1使用的时钟源,这个是在"Clock Configuration"标签里,这里设置为LSI。
然后回到"LPBAM Scenario & Configuration"便签添加DMA的工作队列,这个队列比较简单,使用LPTIM1的PWM上升沿触发DMA向LPGPIO写数据,切换引脚的状态。
在"LPBAM function Toolbox"里选择"Write Pin Sequence"添加。这个就是能够以一定的序列控制IO口。
在queue中添加"Write Pin Sequence"之后,在其中选择引脚为PA1,需要在2中状态间切换,DMA读取数据的数组为"LPGPIOBufferState"。这个数组需要在程序里自己添加。设置触发方式为LPTIM1的CH1。
然后在设置这个queue,让其工作在循环模式下。
最后回到普通模式,为LPBAM分配一个DMA的通道。设置为循环模式,并在NVIC中使能中断,这样当DMA控制器出错的时候能够触发中断。
为了实现更低的功耗,可以将不使用的GPIO设置为Analog模式。
在Keil中的步骤
在工程中重点是如何时候cubemx生成的代码。cubemx会生成3个源文件和1个头文件,需要将头文件添加进main.c中,来调用其API。生成的头文件和源文件的名字和我们在cubemx中的命名有关。
在cubemx中我们使用了一个数组,因此需要自己定义一下。然后在"xxxbuild.c"中引用这个数组。
uint32_t LPGPIOBufferState[2]={
__LPBAM_GPIO_STATE(LPBAM_GPIO_PIN_0, LPBAM_GPIO_PIN_SET),
__LPBAM_GPIO_STATE(LPBAM_GPIO_PIN_0, LPBAM_GPIO_PIN_RESET)
};
还需要定义进入STOP2模式的代码。
static void Enter_Stop2_Mode(void)
{
/* Enter the system to STOP2 mode */
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
/* Check that the system was resumed from stop 2 */
if (__HAL_PWR_GET_FLAG(PWR_FLAG_STOPF) == 0U)
{
Error_Handler();
}
/* Clear stop flag */
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_STOPF);
/* Check that stop flag is cleared */
if (__HAL_PWR_GET_FLAG(PWR_FLAG_STOPF) != 0U)
{
Error_Handler();
}
}
然后调用API,注意API的调用是有顺序的。在进入STOP2之前启用LPBAM对应的外设。退出STOP2后,这些外设不能自动停止,需要手动停止,所以看到在下面还有3行代码。
MX_WriteIo_Init();
MX_WriteIo_WriteReq_Init();
MX_WriteIo_WriteReq_Build();
MX_WriteIo_WriteReq_Link(&handle_LPDMA1_Channel0);
MX_WriteIo_WriteReq_Start(&handle_LPDMA1_Channel0);
Enter_Stop2_Mode();
MX_WriteIo_WriteReq_Stop(&handle_LPDMA1_Channel0);
MX_WriteIo_WriteReq_UnLink(&handle_LPDMA1_Channel0);
MX_WriteIo_WriteReq_DeInit();
重要步骤
到这里还没有完,因为cubemx生成的代码,包括对应外设的初始化,并启用分配的DMA通道,而我们自己使用的外设不会自动启动,需要自己添加代码。比如这个例子中的LPTIM1的启动代码。
跳转到*MX_WriteIo_WriteReq_Start(&handle_LPDMA1_Channel0)*函数,添加LPTIM1的PWM启动代码。
if (HAL_LPTIM_PWM_Start(&hlptim1, LPTIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
同样在* MX_WriteIo_WriteReq_Stop(&handle_LPDMA1_Channel0)*里添加停止代码。
if (HAL_LPTIM_PWM_Stop(&hlptim1, LPTIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
至此就完成了代码功能。
功耗
功耗我自己没有测试,官方文档中提到在上面的例子中,在STOP2模式下,使用LPTIM1和一个LPGPIO的1s周期里的平均功耗为4.2uA。
参考资料
官方提供了一些指导文档:AN5645和AN5816。
cubemx里也提供了LPBAM的指导例程,这个例子就是参考的"LPBAM_LPGPIO_IOToggle"例子。
AN5645_STM32U5 系列使用LPBAM 进行功耗优化.pdf
(2.12 MB, 下载次数: 4)
an5816-how-to-build-stm32-lpbam-application-using-stm32cubemx-stmicroelectronics.pdf
(5.49 MB, 下载次数: 6)