|
LVGL默认是使用回调函数进行事件触发的,比如按下屏幕中的按键,或者是某个控件的值发生变化之类的,而如果想要使用LVGL去连续不断地显示某样东西,那就需要用到lv_timer即定时器控件,这帖我将用lv_timer来实现对BME680模块的温度、湿度、气压数据读取。 首先需要在LVGL程序开始的时候注册定时器回调函数:
void lv_example_tileview(void)$ r* h; z5 @( F* l9 h) m* Q3 { { lv_obj_t *tileview = lv_tileview_create(lv_scr_act()); lv_timer_t * timer1; timer1 = lv_timer_create(my_timer1 , 100000 , NULL); 。。。0 G' S' z) G+ Y$ r }; b; O) }: Q9 n$ [9 p( M 8 R# O Y+ ^- ?/ v5 ^ . |* N$ q q, ?- U* G 第二个参数为定时器溢出时间间隔,数字越大间隔越大。然后需要在定时器回调函数里面写上具体的实现:9 d5 |2 S2 N' K: K- z- R7 O static void my_timer1(lv_timer_t * timer)# {+ s9 e1 w4 n4 U7 Q) ]2 m {6 R. b1 z' Y* D- o: e 。。。3 [+ _2 L0 ^/ X8 }7 D, K+ Y }" T+ u$ S Y3 A8 \; y5 P# k" Q6 i & R; \5 I* D C BME680是一个使用I2C通信总线的模块,非常典型也非常常用,采集的大气压数据非常准确,跟气象台数据基本一致,算是BMP280的上位替代,BME680的初始化代码比较特别,是使用回调函数注册相关参数的,比如I2C读,I2C写,Delay函数,器件地址等,我手上这个模块的器件地址是0x77:
#define BME680_I2C_ADDR_SECONDARY UINT8_C(0x77)& O5 D; ^/ F& F! y% q" j gas_sensor.dev_id = BME680_I2C_ADDR_SECONDARY;0 [* m& k1 B) h% _. H gas_sensor.intf = BME680_I2C_INTF; gas_sensor.read = user_i2c_read;0 O# t8 p3 Y2 {# X2 u6 B7 L gas_sensor.write = user_i2c_write;; j: y4 d6 P4 q; p" a$ A2 r( P- W gas_sensor.delay_ms = Delay_ms; if(bme680_init(&gas_sensor) != BME680_OK) {% J* B1 }1 z' Z/ k* l6 s- X9 P! H. g7 e printf("Error al inicializar el sensor.\n"); }- g" _/ f w1 S; i3 m& X printf("bme680_init.\n");% C5 W2 h( a9 W8 @ ; r& q2 X/ o: `1 Q$ Q4 ?7 V6 J I2C就用现成的模拟I2C代码即可:2 c3 X3 r0 e" W6 _- r3 U
uint8_t Fake_I2C_Read_Reg_Addr(uint8_t dev_addr , uint8_t reg) { uint8_t res; Fake_I2C_Start(); # }( c. a. L. l3 Z0 P# ], e6 q( ^ Fake_I2C_Send_Byte((dev_addr << 1) | 0); Fake_I2C_Wait_Ack(); Fake_I2C_Send_Byte(reg);/ G, p1 Q- |$ c5 G- z. _ Fake_I2C_Wait_Ack(); Fake_I2C_Start();& L& ?- x9 ?9 Y6 a7 E Fake_I2C_Send_Byte((dev_addr << 1) | 1); Fake_I2C_Wait_Ack(); res = Fake_I2C_Read_Byte(0); Fake_I2C_Stop(); return res; ! n$ G+ L- X% V& C }; c1 r/ ]" {/ p+ e1 z: E , A ?3 T. W7 `8 Y9 H, \+ p void Fake_I2C_Read_Reg_Datas(uint8_t dev_addr , uint8_t reg , uint8_t data_len , uint8_t data[]) {6 \/ l$ n) c" h% h; f! E while(data_len) {1 P8 |/ P7 `. O7 B/ O4 I3 e *data = Fake_I2C_Read_Reg_Addr(dev_addr , reg++);4 a+ O% @5 @7 U data ++; data_len --; } Delay_ms(1);6 h% _% Y# p' Z } , z! j u H# m7 T, a4 E void Fake_I2C_Write_Reg_Addr_Data(uint8_t dev_addr , uint8_t reg , uint8_t data)! v7 I) T: s0 c/ }+ X; n { Fake_I2C_Start(); Fake_I2C_Send_Byte((dev_addr << 1) | 0);( R& o) U6 g' E2 ~3 B5 M, h Fake_I2C_Wait_Ack();* }, \) V5 t5 I1 r Fake_I2C_Send_Byte(reg); 9 r y/ a1 z1 C* U+ c Fake_I2C_Wait_Ack(); Fake_I2C_Send_Byte(data); 8 j2 C5 f ?; u, j+ } Fake_I2C_Wait_Ack(); Fake_I2C_Stop(); } 2 L# m) o$ K& d/ m( P3 J+ U# g void Fake_I2C_Write_Reg_Addr_Datas(uint8_t dev_addr , uint8_t reg , uint8_t data_len , uint8_t data[]) {) ]6 {) C$ b9 Q) a) D: ^ int i;% T+ x! q* _1 i/ O# ^ Fake_I2C_Start(); Fake_I2C_Send_Byte((dev_addr << 1) | 0);+ C! c7 f2 x9 V Fake_I2C_Wait_Ack();; ]2 K4 z0 {6 P. R' O8 c' j Fake_I2C_Send_Byte(reg); - b9 C5 u) l- H+ n& C Fake_I2C_Wait_Ack();; k7 U" W( m- I; ?6 T: V: ] for(i = 0 ; i < data_len ; i++)% `& S4 |) V6 u1 J {4 R. k( P# i: b Fake_I2C_Send_Byte(data); Fake_I2C_Wait_Ack(); } Fake_I2C_Stop();/ b* m- Y; Q5 Y! E3 N8 {& S6 H: v$ d Delay_ms(1); } , u5 ~7 o! f6 o8 u5 D int8_t user_i2c_write(uint8_t dev_id , uint8_t reg_addr , uint8_t *reg_data , uint16_t len) {. ]9 V0 F! n$ p1 z" }+ i Fake_I2C_Write_Reg_Addr_Datas(0x77 , reg_addr , len , reg_data);+ B5 J, U7 y8 ~% V return 0; } 3 ]; N) M5 Q. J) }" [3 g int8_t user_i2c_read(uint8_t dev_id , uint8_t reg_addr , uint8_t *reg_data , uint16_t len)* f3 @ h8 e6 R5 j { Fake_I2C_Read_Reg_Datas(0x77 , reg_addr , len , reg_data); return 0;& y4 E& Y" I! J9 C/ q }) ?# |4 M" e Y- u5 o : {* g3 Y" O. ?, x 2 Y- {' L9 `" c8 E/ o" y 在主界面中添加tileview,可以实现类似智能手机的滑屏效果:1 L8 x# Y/ l! L8 A % b1 `2 ?6 t, e- ~ lv_obj_t *tileview = lv_tileview_create(lv_scr_act()); lv_obj_t *tile_1 = lv_tileview_add_tile( tileview, 0 , 0, LV_DIR_LEFT | LV_DIR_RIGHT ); lv_obj_t *tile_2 = lv_tileview_add_tile( tileview, 1 , 0, LV_DIR_LEFT | LV_DIR_RIGHT ); 4 }2 C4 Q$ _2 d! S 这样,就可以单独用一个页面去显示BME680的传感器读数,而其它页面实现别的功能了,非常方便。用Bar(进度条)和Label去动态显示读数: 2 B# O. {# G% L% `- X: ? font = &lv_font_montserrat_24; lv_obj_t* label = lv_label_create(btn_speed_up); lv_obj_set_style_text_font(label , font , LV_PART_MAIN);0 z: C0 h! g; C7 S5 v; v O) @ label_bme680_temp = lv_label_create(tile_1); lv_obj_set_style_text_font(label_bme680_temp , font , LV_STATE_DEFAULT); lv_obj_align(label_bme680_temp , LV_ALIGN_CENTER, 0 , -220);/ g" I; n% r" a% S% x/ o, A' B bar_bme680_temp = lv_bar_create(tile_1); lv_obj_align(bar_bme680_temp , LV_ALIGN_CENTER, 0 , -180); lv_obj_set_size(bar_bme680_temp , 260 , 20); label_bme680_pres = lv_label_create(tile_1);" |0 G7 E5 W# |- V$ I/ h lv_obj_set_style_text_font(label_bme680_pres , font , LV_STATE_DEFAULT);$ \+ j; h8 u5 d& `% c lv_obj_align(label_bme680_pres , LV_ALIGN_CENTER, 0 , -120);2 s' x6 O( s V% T! g bar_bme680_pres = lv_bar_create(tile_1);) w( R" h# p' [9 L lv_obj_align(bar_bme680_pres , LV_ALIGN_CENTER, 0 , -80); lv_obj_set_size(bar_bme680_pres , 260 , 20); label_bme680_humi = lv_label_create(tile_1);! o8 { A: H5 q* q: m lv_obj_set_style_text_font(label_bme680_humi , font , LV_STATE_DEFAULT);! w8 d, w6 S; ?" e4 ~/ p w# n lv_obj_align(label_bme680_humi , LV_ALIGN_CENTER, 0 , -20);; F/ ~6 A* g* t, l6 m! n 7 S2 d7 C0 c: w- h8 o bar_bme680_humi = lv_bar_create(tile_1);* _: g) X" J& `. H lv_obj_align(bar_bme680_humi , LV_ALIGN_CENTER, 0 , 20);3 j0 F( @/ C6 P& G' I lv_obj_set_size(bar_bme680_humi , 260 , 20); . G/ l: b6 x* r3 K+ \* g 看看演示效果:* O4 t- i. \2 D& r 用手捂着BME680,会看到温度读数升高,松开温度则降下来。: e7 F* B. | v, Z B # ~* I6 y- d4 W 除了使用LVGL界面以及lv_timer定时器定时采集BME680数据并显示以外,我还用STM32H743VI的GPIO串口功能接一个串口语音模块,结合LVGL界面实现语音控制,涉及到中断跳转以及lv_timer定时器回调机制。7 T$ b5 I4 e% X; @! T% w( N# j 串口语音模块直接买的天问模块,接开发板的UART2即PA2和PA3接口:
串口的空闲中断及DMA实现如下:- G9 `8 L) ?, K- o1 Q UART_HandleTypeDef g_uart2_handle;, b& ^# g' A+ j3 R9 x2 _! v7 t7 ]8 J DMA_HandleTypeDef hdma_uart2_rx;+ D& r% C2 R. ~ O/ z5 A7 y+ ` uint8_t uart2_recv_end_flag; uint32_t rx2_len = 0;6 Q% X+ m0 I- X# y, f$ j unsigned char rx2_buf[BUFFERSIZE]; bool uart2_flag1 = false; bool uart2_flag2 = false; ( B% W9 F: b- y' \# f void UART2_Init(uint32_t baud) {* V- \5 h6 \! y7 W5 e& O- O0 C) G GPIO_InitTypeDef gpio_init_struct;9 b* G. \9 {' C! x2 @ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART2_CLK_ENABLE();5 e2 y) }' k" E) A3 r __HAL_RCC_DMA1_CLK_ENABLE();1 x- H! Z' N, J' ^7 O gpio_init_struct.Mode = GPIO_MODE_AF_PP;: M1 x# {$ C! r" \! T gpio_init_struct.Pull = GPIO_PULLUP; gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;2 }1 z) s! Y- H" x, ]5 S) ~( m gpio_init_struct.Alternate = GPIO_AF7_USART2; % ~* ?( Q- g7 U' ~. g3 I$ v: q gpio_init_struct.Pin = GPIO_PIN_2 | GPIO_PIN_3; ; U9 f: g* |3 e' h8 F# S. c HAL_GPIO_Init(GPIOA , &gpio_init_struct);! V m7 T" t/ L" r0 ] g_uart2_handle.Instance = USART2; g_uart2_handle.Init.BaudRate = baud;: ?4 q2 y, J. ^. j7 K6 z1 h g_uart2_handle.Init.WordLength = UART_WORDLENGTH_8B; g_uart2_handle.Init.StopBits = UART_STOPBITS_1;7 f; v( E8 a C B" K- |3 V4 T g_uart2_handle.Init.Parity = UART_PARITY_NONE;% d) B: R& i$ J% } z g_uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;: ]* b: t3 y8 Y) w# a g_uart2_handle.Init.Mode = UART_MODE_TX_RX; HAL_UART_Init(&g_uart2_handle); 3 L- y* D( s5 I1 H5 s) \* f3 N" T6 w __HAL_UART_ENABLE_IT(&g_uart2_handle , UART_IT_IDLE);% W: {- m/ l- r3 D3 d! q$ |! h HAL_NVIC_SetPriority(USART2_IRQn , 0 , 0); HAL_NVIC_EnableIRQ(USART2_IRQn); 2 T6 c' d! ^: A hdma_uart2_rx.Instance = DMA1_Stream0;( ^9 d$ [ P" X D4 |0 ? hdma_uart2_rx.Init.Request = DMA_REQUEST_USART2_RX;/ F" \) s% i$ @% S5 i hdma_uart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_uart2_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_uart2_rx.Init.MemInc = DMA_MINC_ENABLE;! _! s& z) o. I0 j9 u) Q/ K4 v hdma_uart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_uart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;. ~: i" ^5 I1 o) F# B; L: H hdma_uart2_rx.Init.Mode = DMA_CIRCULAR;" b2 Q, C" s1 A+ d; d hdma_uart2_rx.Init.Priority = DMA_PRIORITY_LOW;2 R( i" b4 F- e1 H2 H8 `0 R6 L hdma_uart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&hdma_uart2_rx); 1 r d: t( p% p/ P& D __HAL_LINKDMA(&g_uart2_handle , hdmarx , hdma_uart2_rx);' B3 J2 ?* i3 L- r6 s - G! f0 V/ O1 i& d# g } STM32H743没有将DMA通道和特定外设绑定,因此DMA通道选择别的也是可以的(但最好是选择DMA1)。' P1 [! G% W! k3 }" G3 } 串口中断服务函数实现: void USART2_IRQHandler() { if(__HAL_UART_GET_FLAG(&g_uart2_handle, UART_FLAG_IDLE)) {% s: n% M( K+ S g I j: @ __HAL_UART_CLEAR_IDLEFLAG(&g_uart2_handle); & z! E) U- l3 M; T# m3 a; k // g_uart2_handle.Instance->ISR;5 Y8 a; K# x: N' v5 K) ~( R // g_uart2_handle.Instance->RDR; // printf("USART2_IRQHandler\n");/ ] ~, @* h8 A0 e$ f' q" {# e2 \ HAL_UART_DMAStop(&g_uart2_handle); SCB_InvalidateDCache_by_Addr((uint32_t*)rx2_buf , BUFFERSIZE);( T, V: _/ f0 U2 L# K4 z( R rx2_len = BUFFERSIZE - __HAL_DMA_GET_COUNTER(&hdma_uart2_rx);3 b1 f! k9 J L# o: ^- s" h' {3 p/ z uart2_recv_end_flag = 1;! ^! @" F* K% Y' l rx2_buf[rx2_len] = '\0'; // printf("USART2_IRQHandler rx2_buf = %s rx2_len = %d.\n" , rx2_buf , rx2_len); if(strstr(rx2_buf , "打开一号灯") != NULL) { printf("USART2_IRQHandler 打开一号灯.\n" , rx2_buf , rx2_len);: l, w: T/ U( z6 C8 u. a* [ uart2_flag1 = true; } else if(strstr(rx2_buf , "关闭一号灯") != NULL)' C8 y J# ?- O/ @ { printf("USART2_IRQHandler 关闭一号灯.\n" , rx2_buf , rx2_len); uart2_flag1 = false;# U" `9 o+ Y% z/ w } else if(strstr(rx2_buf , "打开二号灯") != NULL) { printf("USART2_IRQHandler 打开二号灯.\n" , rx2_buf , rx2_len); uart2_flag2 = true; }9 x5 t9 l4 V' ` else if(strstr(rx2_buf , "关闭二号灯") != NULL) { printf("USART2_IRQHandler 关闭二号灯.\n" , rx2_buf , rx2_len);4 m+ f/ X+ p- z0 b0 a% p uart2_flag2 = false; } HAL_UART_Receive_DMA(&g_uart2_handle , (unsigned char*)rx2_buf , BUFFERSIZE); } }& A! h' m; f+ Y! B6 D [, ? ' J7 U- G% {. O' e) } J% f7 v 在这里实现相关的标志位控制,以便在lv_timer定时器回调函数中控制相应组件或者外设。lv_timer定时器回调函数: static void my_timer1(lv_timer_t * timer) { 。。。! V. w8 x% g- t, d if(uart2_flag1)2 @; \; D1 J2 A; d4 y# `- f { lv_led_on(lv_led1);6 M6 n5 ~5 O8 v1 `9 R }" U- ^# ~: v" N2 M/ m5 ~ else 7 R& y, y* {% d2 D! X; _0 t { lv_led_off(lv_led1); }+ @. _! M1 z4 O1 b. Y if(uart2_flag2) { X& U5 H7 B l& L1 B: ]; [- }& p, ] lv_led_on(lv_led2); } else { lv_led_off(lv_led2);& n- z& K! C8 V6 T3 Q1 @- G }6 |0 h& h& c8 j! e& P" z" J8 j, D } 视频演示效果:, K5 }% f* t) v# S6 K3 l$ W0 S |
STM32C542RC开发板的按键控制LED闪烁实验
【 逢7发帖赢大礼】1、利用CubeMX生成正点原子H7R7开发板的STM32CubeIDE工程
《STM32CubeMX2 实战课程》学习笔记--课程1:STM32CubeMX2安装
【STM32H735G-DK测评】ADC3+TIMER1+DMA 采样模拟信号
【STM32H735DK测评】Ethernet+lwip DHCP获取IP地址
STM32H743基于CubeMx5.0配置手册
STM32L562DK探索板——串口通讯测试成功
【STM32H735G-DK测评】lvgl移植
【STM32H735G-DK 测评】STM32H735G-DK External Loder 编译和测试
M24LR-discovery测试小结
微信公众号
手机版