本帖最后由 风子 于 2015-11-8 16:16 编辑
LPUART 即 LOW POWER UART,是STM32L4系列专为低功耗设计的低功耗外设之一,既然玩低功耗的板子,当然不能少了捣鼓低功耗功能,这次就玩玩LPUART。
同时,在上一篇帖子【NUCLEO-L476RG LL库开发】STM32【LL库】开发使用指南中初步介绍了 STM32 LL库以及它的独立使用方法,这个帖子顺便演示一下可能是更通用(实用)的使用方法:LL库和HAL库混合使用。毕竟HAL库使用CUBEMX,非常方便,而独立使用LL库还需要自己建立工程,况且LL库本来就是和HAL库集成在一起的,相互配合,优势互补,最大限度的兼顾用户编程使用方便和MCU性能功耗,我想这才是ST设计这个库的初衷。不过这不是这个帖子的重点,不讲,只在代码中演示。
实验目的是测试LPUART的唤醒Stop mode 2功能,要做的工作如下:
1.硬件,nucleo的虚拟串口默认接的是USART2,要使用LPUART,需要重新接线
2.软件,配置好LPUART,并且重定向printf函数,便于输出需要的信息
3.配置进入Stop mode 2的准备工作
4.进入Stop mode 2
5.使用LPUART唤醒MCU
6.输出一些过程中观察需要的调试信息
大概就有这么些内容,这个实验是参考了STM32L4Cube里面的一个例子,由于水平有限,这也只是个人实验笔记,可能有些地方写得不好,大家自己去看官方库里面的例程,位于STM32Cube_FW_L4_V1.1.0\Projects\STM32L476RG-Nucleo\Examples_LL\LPUART\LPUART_WakeUpFromStop2文件夹中。下面是具体操作:
1.板载ST-LINK有虚拟串口功能,并且引出了TX,RX引脚,只需要把他们和LPUART1的RX,TX引脚连起来即可,注意板子上的TX,RX是针对ST-LINK端说的,所以连接时和LPUART的TX,RX反过来连接,如图
注意RX,TX上连了其他USART的时候千万不要同时使用USART2,本来使用其他串口的时候应该断开板子上SB13,SB14,闲麻烦就不断开了,别使用USART2就好。
2.1配置LPUART1,这个使用Cubemx来完成,比较简单
打开LPUART1,配置PC0,PC1为LPUART1_RX,LPUART1_TX,打开RCC 外部低速时钟LSE
系统时钟选HSI经PLL倍频,80M,LPUART时钟选择LSE,这里不能使用PCLK,因为进入stop mode 2后PLL,系统时钟会被关闭,相应外设不能工作,所以此处只能选择HSI或者LSE,如果用HSI,上一步可以不必打开LSE。
LPUART1参数:波特率9600,使用LSE时钟波特率最高只能9600,其他默认。
打开LPUART的中断。
配置好后就可以直接生成工程了,我使用的是MDK5.16a,生成MDKv5工程。
2.2,重定向printf到串口LPUART1
printf函数非常方便,所以用它来输出调试信息,打开上一步生成的工程,在合适的地方加入代码: - #ifdef __GNUC__
- #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
- #else
- #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
- #endif
- PUTCHAR_PROTOTYPE
- {
- HAL_UART_Transmit(&hlpuart1, (uint8_t *)&ch, 1, 0xFFFF);
- return ch;
- }
复制代码- //main中
- printf("This is a test!\n");
复制代码
最后一句是放在主函数中,用来测试下printf函数能不能正常输出信息
看起来一切正常,可以下一步了。
2.3先来输出一些信息,供后面比较,输出系统时钟信息:
在前面配置中,我们使用了PLL倍频,得到80M的系统时钟,而在进入stop mode 2后PLL会被关闭,重新唤醒之后只能使用HSI或者MSI作为系统时钟,PLL需要重新使能才能使用,就像系统刚复位的时候一样,所以进入停止模式之前,应该先设置好唤醒之后的系统时钟,这是准备工作之一,
下面的函数用于获取当前系统时钟设置: - void HAL_RCC_GetClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t *pFLatency)
复制代码获得时钟信息后用printf函数输出:
现在的时钟是PLL,和之前的配置相符。
3.进入停止模式2之前准备工作
为了使MCU能正常进入停止模式,且能正常被唤醒,有些工作必须做好,不然就睡死过去醒不来了。
3.1打开PWR时钟,设置系统唤醒后时钟为MSI - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
- LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_MSI);
复制代码
3.2开中断,在HAL_UART_MspInit中加入代码: - /* USER CODE BEGIN LPUART1_MspInit 1 */
- LL_LPUART_SetWKUPType(LPUART1, LL_LPUART_WAKEUP_ON_RXNE);//设置唤醒方式为RXNE
- LL_LPUART_EnableIT_RXNE(LPUART1);//打开RXNE中断
- LL_LPUART_EnableIT_WKUP(LPUART1);//打开wakeup中断
- /* USER CODE END LPUART1_MspInit 1 */
复制代码
3.3检查LPUART的一些状态位,这个我就直接复制了例程中的一个函数,稍加修改 - __STATIC_INLINE void PrepareLPUARTToStopMode(void)
- {
- /* Empty RX Fifo before entering Stop Mode 2 (Otherwise, characters already present in FIFO
- will lead to immediate wake up */
- while (LL_LPUART_IsActiveFlag_RXNE(LPUART1))
- {
- /* Read Received character. RXNE flag is cleared by reading of RDR register */
- ubReceivedChar=LL_LPUART_ReceiveData8(LPUART1);
- }
- /* Clear OVERRUN flag */
- LL_LPUART_ClearFlag_ORE(LPUART1);
- /* Make sure that no LPUART transfer is on-going */
- while(LL_LPUART_IsActiveFlag_BUSY(LPUART1) == 1)
- {
- }
- /* Make sure that LPUART is ready to receive */
- while(LL_LPUART_IsActiveFlag_REACK(LPUART1) == 0)
- {
- }
- /* About to enter stop mode: switch off LED */
- LL_GPIO_ResetOutputPin(GPIOA, LL_GPIO_PIN_5);
- /* Configure LPUART1 transfer interrupts : */
- /* Clear WUF flag and enable the UART Wake Up from stop mode Interrupt */
- LL_LPUART_ClearFlag_WKUP(LPUART1);
- LL_LPUART_EnableIT_WKUP(LPUART1);
- /* Enable Wake Up From Stop */
- LL_LPUART_EnableInStopMode(LPUART1);
- }
复制代码分析一下这个函数都干了些什么(其实代码里有注释):
a.不断读取接收数据,知道RXNE为0,确保已经没有数据发进来
b.清除OVERRUN标志,确保所有当前工作完成,进入可以接收的状态 c.清除WUF标志位,开WKUP中断(上一步自己做了),使能wakeup功能 4.一切准备工作就绪,MCU可以进入停止模式了, - PrepareLPUARTToStopMode();
- HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
复制代码不过还有一个问题,还没写中断函数呢,MCU醒来后不知道干什么啊,人早上醒来知道洗脸刷牙,机器可不知道,所以得告诉它,写中断服务函数。
剩下的更新在一楼,完整工程见附件:
STM32L476_HAL LL_LPUART.rar
(2.88 MB, 下载次数: 640)
|
继续更新:
为方便观察,进入停止模式之前先让LED闪烁3秒钟,同时输出一些信息。接着写中断函数
当有中断发生,先检查中断源是不是WKUP中断,并且是否使能该中断,如果不是,则忽略掉。
检查确定是WKUP中断,则关闭WKUP中断,清除相应标志位,避免重复进中断,最后将接收到的数据读出来存放,以便后用。
中断结束,MCU已经被唤醒,会回到进入停止模式的地方继续执行后面的代码,后面也不用做什么事,就是把收到的字符发送回串口助手,这样我们就可以知道MCU已经成功被唤醒了,当然,唤醒后直接把LED打开,观察更方便代码也更简单。同时发送系统时钟的信息和进入停止模式之前的信息做个比较,
可以清楚的看到,我们发送到MCU的字符'a'成功发送回来,进入停止模式前系统时钟是PLL,而唤醒之后变成了MSI,如果要继续使用PLL,需要重新配置使能,如果不影响其他地方,可以简单的再调用一次cube生成的时钟配置函数:
最后,把唤醒后的时钟改为HSI,测试一下效果:
此时唤醒后系统时钟就是HSI。
那样做结果是把两个串口链接在同一根线上,通信会相互干扰,你看原理图就懂了
呵呵
谢谢支持
多谢分享。。。
好像挺划算的,哈哈