|
LVGL默认是使用回调函数进行事件触发的,比如按下屏幕中的按键,或者是某个控件的值发生变化之类的,而如果想要使用LVGL去连续不断地显示某样东西,那就需要用到lv_timer即定时器控件,这帖我将用lv_timer来实现对BME680模块的温度、湿度、气压数据读取。 首先需要在LVGL程序开始的时候注册定时器回调函数:1 r4 n. V5 l$ Z' q* d
void lv_example_tileview(void)1 J5 S7 z& t3 q1 U# g& M/ Z {! q7 n7 r0 I( d" u lv_obj_t *tileview = lv_tileview_create(lv_scr_act());) K4 X/ T7 O5 \7 T$ p) W9 Q lv_timer_t * timer1;1 Z% k/ \8 K) L timer1 = lv_timer_create(my_timer1 , 100000 , NULL); 。。。 }- l$ q# ]2 b0 U( l1 Q ! H3 h$ g1 Q( E9 B 第二个参数为定时器溢出时间间隔,数字越大间隔越大。然后需要在定时器回调函数里面写上具体的实现: static void my_timer1(lv_timer_t * timer)5 Y9 q5 r' q4 _, I9 Q$ T: V {7 `0 ^8 }: ~9 v3 J1 E) _% o 。。。! J! R/ ]6 V$ s9 o }, A$ {9 f1 g6 S6 g7 X2 f) J3 ^' m3 w7 q 6 ? k- c: R! z _4 N; F# m BME680是一个使用I2C通信总线的模块,非常典型也非常常用,采集的大气压数据非常准确,跟气象台数据基本一致,算是BMP280的上位替代,BME680的初始化代码比较特别,是使用回调函数注册相关参数的,比如I2C读,I2C写,Delay函数,器件地址等,我手上这个模块的器件地址是0x77:
#define BME680_I2C_ADDR_SECONDARY UINT8_C(0x77) gas_sensor.dev_id = BME680_I2C_ADDR_SECONDARY;& K# c- k% @/ E9 x gas_sensor.intf = BME680_I2C_INTF;- {& Q- z) {4 z9 o gas_sensor.read = user_i2c_read;. C7 a& c$ b( ?0 } gas_sensor.write = user_i2c_write; gas_sensor.delay_ms = Delay_ms;6 w% v: Y( N) M5 _5 Z if(bme680_init(&gas_sensor) != BME680_OK) { printf("Error al inicializar el sensor.\n"); } printf("bme680_init.\n"); & P$ [ S/ U! \+ m+ [- z 8 ]7 ~5 Z* w% b+ f7 R3 E I2C就用现成的模拟I2C代码即可:- p& ?' s/ [- [* {' r* U
uint8_t Fake_I2C_Read_Reg_Addr(uint8_t dev_addr , uint8_t reg)" t4 g4 N" }9 E+ R { uint8_t res;' B9 H) {5 o1 E3 k9 I9 l Fake_I2C_Start(); ' E- K# q0 F. q* ?1 `5 A Fake_I2C_Send_Byte((dev_addr << 1) | 0);) f1 t8 @& h+ t; f$ n9 \ Fake_I2C_Wait_Ack(); Fake_I2C_Send_Byte(reg); Fake_I2C_Wait_Ack(); Fake_I2C_Start();; Z: M( W; {2 n" f Fake_I2C_Send_Byte((dev_addr << 1) | 1); Fake_I2C_Wait_Ack();. Z( c: d2 Y* g/ ]$ L4 z \7 Y res = Fake_I2C_Read_Byte(0); Fake_I2C_Stop();, L) |, o1 f+ ]; q return res; , E. w- r& \ s( b# q4 \! `- \ }6 E- H+ y$ }' h" K 2 w# H) {" r: c' D8 W void Fake_I2C_Read_Reg_Datas(uint8_t dev_addr , uint8_t reg , uint8_t data_len , uint8_t data[])2 @0 c' U) Q2 {' p' F* H$ H { while(data_len) { *data = Fake_I2C_Read_Reg_Addr(dev_addr , reg++);6 O; F, }! X0 Q. J9 }$ I! ~ data ++;" k' S& d8 g0 ~, Q/ ~& s3 ~4 e data_len --;3 o9 Y: o9 B+ b# k& U% p# G; J } ( u$ R6 V( f# |7 I) ]. S Delay_ms(1);8 I9 y* i7 _. }0 t9 Y+ m }$ e$ i9 [& g+ R- e' q8 Y " Q" Y) ~4 Z. S) j9 X" g$ {' S ' ~) }1 O+ L* _ void Fake_I2C_Write_Reg_Addr_Data(uint8_t dev_addr , uint8_t reg , uint8_t data) {+ @) T/ ^" @1 R4 P6 H0 ? Fake_I2C_Start(); Fake_I2C_Send_Byte((dev_addr << 1) | 0); Fake_I2C_Wait_Ack();# g. k! F& p, G4 A Fake_I2C_Send_Byte(reg); Fake_I2C_Wait_Ack();7 G n* d5 M7 t, R" q Fake_I2C_Send_Byte(data); % U! `& z2 _) w Fake_I2C_Wait_Ack();. }9 o5 F I0 k) }( M Fake_I2C_Stop();0 h. m) t9 C4 {% _ w }: s& ?+ b" W- `% Q ' u+ y& f3 ~4 }9 |& M void Fake_I2C_Write_Reg_Addr_Datas(uint8_t dev_addr , uint8_t reg , uint8_t data_len , uint8_t data[])7 K4 \# N6 O0 z$ F { int i; Fake_I2C_Start(); Fake_I2C_Send_Byte((dev_addr << 1) | 0);& `. F7 v5 q8 a/ q! Y; j6 K1 } Fake_I2C_Wait_Ack(); Fake_I2C_Send_Byte(reg); Fake_I2C_Wait_Ack();) n) Y& K" Y" A8 v9 {% N: t# ^ for(i = 0 ; i < data_len ; i++)4 Z, j/ q: q' Z5 l0 F! b5 k. E {, m5 O( |/ S, }9 L \ Fake_I2C_Send_Byte(data); Fake_I2C_Wait_Ack(); }3 w- A( Z7 r- U4 a( t' D Fake_I2C_Stop(); Delay_ms(1); }2 X; z* k& v% I ]! q8 a ! F+ U1 T9 P" z int8_t user_i2c_write(uint8_t dev_id , uint8_t reg_addr , uint8_t *reg_data , uint16_t len) { Fake_I2C_Write_Reg_Addr_Datas(0x77 , reg_addr , len , reg_data); return 0;+ X. L( S2 o6 @' U2 T$ y }% s1 b* Q4 I6 g/ z int8_t user_i2c_read(uint8_t dev_id , uint8_t reg_addr , uint8_t *reg_data , uint16_t len) {7 n5 X4 q C3 I/ f$ k( j9 v) i* l Fake_I2C_Read_Reg_Datas(0x77 , reg_addr , len , reg_data);4 j, B5 k) E+ |' {2 }6 V Q4 V return 0;$ m' R& \& k, V! R% p/ | }, G2 H7 W1 k4 h; T% f9 M1 k ) U) E3 g# h1 X( C0 w$ p 在主界面中添加tileview,可以实现类似智能手机的滑屏效果: lv_obj_t *tileview = lv_tileview_create(lv_scr_act()); 0 l# r: p5 e9 g7 R8 v 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 ); - |! g {' j* E7 x( T7 H - g& }3 Q I+ N# N7 {! B 这样,就可以单独用一个页面去显示BME680的传感器读数,而其它页面实现别的功能了,非常方便。用Bar(进度条)和Label去动态显示读数:. F' z) k( D8 q2 _8 L/ ~2 A$ @( G , W% ^! O6 o( H& l, J" g . ?3 Z ?! W+ k+ n$ Z font = &lv_font_montserrat_24; lv_obj_t* label = lv_label_create(btn_speed_up);. @+ B& E# U$ R+ ?0 K( y8 @' X lv_obj_set_style_text_font(label , font , LV_PART_MAIN);% R7 s% E/ c" m) L label_bme680_temp = lv_label_create(tile_1); lv_obj_set_style_text_font(label_bme680_temp , font , LV_STATE_DEFAULT);3 ~" h0 S# y. ^6 ]: h+ j: J lv_obj_align(label_bme680_temp , LV_ALIGN_CENTER, 0 , -220); bar_bme680_temp = lv_bar_create(tile_1);" m9 u- k$ P. B y/ a- J; p* v; F% Q lv_obj_align(bar_bme680_temp , LV_ALIGN_CENTER, 0 , -180); lv_obj_set_size(bar_bme680_temp , 260 , 20);5 ~/ w/ t3 l0 i# r; _" p # P8 R; a/ i# a# t4 G: l label_bme680_pres = lv_label_create(tile_1); lv_obj_set_style_text_font(label_bme680_pres , font , LV_STATE_DEFAULT);* _. O* x- G7 c# o* D" r lv_obj_align(label_bme680_pres , LV_ALIGN_CENTER, 0 , -120); bar_bme680_pres = lv_bar_create(tile_1); lv_obj_align(bar_bme680_pres , LV_ALIGN_CENTER, 0 , -80); lv_obj_set_size(bar_bme680_pres , 260 , 20); % l5 k+ F" `% y4 h' R' H/ } label_bme680_humi = lv_label_create(tile_1); lv_obj_set_style_text_font(label_bme680_humi , font , LV_STATE_DEFAULT); lv_obj_align(label_bme680_humi , LV_ALIGN_CENTER, 0 , -20); u+ |: y( } u% m$ l bar_bme680_humi = lv_bar_create(tile_1);: G0 F% v3 K& W3 y% K# V2 r, C; X lv_obj_align(bar_bme680_humi , LV_ALIGN_CENTER, 0 , 20);3 a4 ~; ^9 e% C0 L' p9 `/ ` lv_obj_set_size(bar_bme680_humi , 260 , 20); b& M: o& \, H/ \* z) i1 z 看看演示效果:* K" O4 a4 `/ B; s 用手捂着BME680,会看到温度读数升高,松开温度则降下来。& r# e# d" _& d# d5 y 除了使用LVGL界面以及lv_timer定时器定时采集BME680数据并显示以外,我还用STM32H743VI的GPIO串口功能接一个串口语音模块,结合LVGL界面实现语音控制,涉及到中断跳转以及lv_timer定时器回调机制。 串口语音模块直接买的天问模块,接开发板的UART2即PA2和PA3接口:2 t; N$ M( O: Q% R5 w9 n4 u4 ]
串口的空闲中断及DMA实现如下: UART_HandleTypeDef g_uart2_handle;6 w6 S! p( \. s t$ C' x- j4 Q DMA_HandleTypeDef hdma_uart2_rx; uint8_t uart2_recv_end_flag; uint32_t rx2_len = 0; unsigned char rx2_buf[BUFFERSIZE];/ U# i* A+ o6 Q1 l: K! m: d- q bool uart2_flag1 = false; bool uart2_flag2 = false;) D" d/ u0 Q( D" v9 q void UART2_Init(uint32_t baud)' f: S4 f4 E7 B0 O {5 Z! L' W- Z- U; [ Q GPIO_InitTypeDef gpio_init_struct; 3 x* G6 ~( o8 f- [ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART2_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE(); : X3 B/ C: T+ a: V& o( l gpio_init_struct.Mode = GPIO_MODE_AF_PP; gpio_init_struct.Pull = GPIO_PULLUP;1 Y+ b1 T5 \! P$ y gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;2 v* T5 y+ L* p8 ~, e gpio_init_struct.Alternate = GPIO_AF7_USART2;; `' w; x! b, v5 W" _8 { gpio_init_struct.Pin = GPIO_PIN_2 | GPIO_PIN_3; HAL_GPIO_Init(GPIOA , &gpio_init_struct);% X# y) P% J$ C' @" c8 j/ b \5 ^2 y$ { ; q& @0 o: e# v; f: \ g_uart2_handle.Instance = USART2; g_uart2_handle.Init.BaudRate = baud; g_uart2_handle.Init.WordLength = UART_WORDLENGTH_8B; g_uart2_handle.Init.StopBits = UART_STOPBITS_1; g_uart2_handle.Init.Parity = UART_PARITY_NONE;/ }2 l- v* f; w( n g_uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; g_uart2_handle.Init.Mode = UART_MODE_TX_RX; ! O. `) u, t% J HAL_UART_Init(&g_uart2_handle);: D/ r$ {, B: ~! k+ R; f% Z \4 g3 `& E8 `+ { __HAL_UART_ENABLE_IT(&g_uart2_handle , UART_IT_IDLE); HAL_NVIC_SetPriority(USART2_IRQn , 0 , 0); HAL_NVIC_EnableIRQ(USART2_IRQn);- n7 ]2 B, K* e% @0 X - [( Z1 b4 n6 L/ E hdma_uart2_rx.Instance = DMA1_Stream0;0 W' @ M. {. S1 Z* Y9 ? hdma_uart2_rx.Init.Request = DMA_REQUEST_USART2_RX; 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; hdma_uart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;! Q$ X- u/ ~1 Z- v& @8 P2 s! S hdma_uart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_uart2_rx.Init.Mode = DMA_CIRCULAR; hdma_uart2_rx.Init.Priority = DMA_PRIORITY_LOW;0 e- S: E7 p1 U% B3 q! J/ ]7 p hdma_uart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;! W1 h4 z( {- ^- R HAL_DMA_Init(&hdma_uart2_rx); ~2 z! z$ j, i, L9 Y1 y' A& g4 g __HAL_LINKDMA(&g_uart2_handle , hdmarx , hdma_uart2_rx); } 8 k4 y7 y5 \3 _+ G* i" z STM32H743没有将DMA通道和特定外设绑定,因此DMA通道选择别的也是可以的(但最好是选择DMA1)。 串口中断服务函数实现: void USART2_IRQHandler()/ q3 E F" L/ h7 W/ V {# V5 C5 n& Y0 `' J if(__HAL_UART_GET_FLAG(&g_uart2_handle, UART_FLAG_IDLE))) B1 i- [, b- d: V/ A% e8 H {4 X$ x' D0 i7 `3 D/ ~. X __HAL_UART_CLEAR_IDLEFLAG(&g_uart2_handle);! Y! S8 d! D$ y/ A+ O2 i* g# R' u // g_uart2_handle.Instance->ISR; // g_uart2_handle.Instance->RDR; // printf("USART2_IRQHandler\n");- w+ h+ g, b- n( j- j HAL_UART_DMAStop(&g_uart2_handle);4 M) C l0 g/ Y % n5 N7 a* q6 {( e5 M' }# r SCB_InvalidateDCache_by_Addr((uint32_t*)rx2_buf , BUFFERSIZE);5 q K! L9 I8 m( d6 J0 Q5 z rx2_len = BUFFERSIZE - __HAL_DMA_GET_COUNTER(&hdma_uart2_rx);; E) |" h! _, m+ q uart2_recv_end_flag = 1; rx2_buf[rx2_len] = '\0';+ x4 c' h* H/ n" ~ # Y" H/ j& u# ^. h; f // printf("USART2_IRQHandler rx2_buf = %s rx2_len = %d.\n" , rx2_buf , rx2_len); if(strstr(rx2_buf , "打开一号灯") != NULL)' j W6 \# j" T7 R: A ~# ~ { printf("USART2_IRQHandler 打开一号灯.\n" , rx2_buf , rx2_len);" N4 w+ l$ P2 y9 f- N# g4 @ t uart2_flag1 = true;( @: Q* m9 k X- f% { }& e2 D5 V: @' q5 u( ^6 z2 z else if(strstr(rx2_buf , "关闭一号灯") != NULL). S. y0 C( c3 P {+ Y ~3 b0 F) G8 e( }9 m0 P" b* J printf("USART2_IRQHandler 关闭一号灯.\n" , rx2_buf , rx2_len); uart2_flag1 = false; } else if(strstr(rx2_buf , "打开二号灯") != NULL) { printf("USART2_IRQHandler 打开二号灯.\n" , rx2_buf , rx2_len); uart2_flag2 = true; } else if(strstr(rx2_buf , "关闭二号灯") != NULL) { printf("USART2_IRQHandler 关闭二号灯.\n" , rx2_buf , rx2_len);# b/ V0 B& ]+ s uart2_flag2 = false;/ {$ g6 n! V% c9 `: a9 ? } HAL_UART_Receive_DMA(&g_uart2_handle , (unsigned char*)rx2_buf , BUFFERSIZE);$ A6 a3 n) B8 m% v- j }% ~- M7 `6 [5 W2 q3 _. v1 ^1 F }1 N- j* ~. O3 ~9 S, z8 D 在这里实现相关的标志位控制,以便在lv_timer定时器回调函数中控制相应组件或者外设。lv_timer定时器回调函数: + U- S1 H: [- g, E; D# F; S/ _ static void my_timer1(lv_timer_t * timer) {9 y9 [8 m$ a- o 。。。2 o5 h. I3 \/ Q l5 I4 W* B if(uart2_flag1) { ]- {6 k8 K& F% h lv_led_on(lv_led1);: J# s% X( b5 Z: H1 w8 b }' r7 u6 X% t4 Y else { lv_led_off(lv_led1); } if(uart2_flag2)$ H Y9 C! E3 _3 ^ { lv_led_on(lv_led2); } else {) X6 o, y r9 F c9 a lv_led_off(lv_led2);! L$ f Z! F* |, L& [! s6 B }) q6 z8 d. r9 I }( X' v& J, T6 u1 u5 b8 i2 C" L 视频演示效果: |
STM32C542RC开发板启用SPI驱动TFT显示屏
STM32C542RC开发板驱动TFT显示屏
【STM32C5教程】(9)DAC 模拟量信号输出实验
STM32C542RC开发板Stop、Sleep和standby模式测试
【STM32C5教程】(10)LPUART DMA 通信实验
【STM32C5教程】(8)ADC 模拟量数据采集实验
【STM32C5教程】(7)TIM1 互补 PWM 输出实验
【STM32C5教程】(6)定时器 PWM 输出实验
【STM32C5教程】(5)定时器周期中断实验
【STM32C5教程】(4)GPIO 外部中断实验
微信公众号
手机版