你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【2025·STM32峰会】GUI解决方案实训分享4-使用MVP架构从硬件外设读取数据并显示到图形界面、从图形界面发送指令控制硬件外设

[复制链接]
donatello1996 发布时间:2025-5-22 00:22
前面做了一帖关于TouchGFX搭建工程的铺垫,但是最终还是要实现与硬件外设进行交互的,TouchGFX的架构中,与硬件外设进行交互的架构称之为MVP架构,即Model类(Modlistener类)、ScreenView类以及ScreenPresenter类,其中Model类和ScreenView类可以直接通过include "main.h"的方式直接控制HAL库的硬件外设,也就是可以在ScreenView类的控件回调函数中直接调用HAL库控制语句,达到控制硬件外设的目的。那么,怎么从硬件外设处循环读取某些信息,比如读取AD采集值,读取SPI接口的2.4G无线模块接收到的信息呢?方法有二,第一是在Model类的tick成员函数中进行读取,第二是在TouchGFX Disigner中创建tick回调事件之后,在ScreenView类的tick回调事件函数中进行读取,前者读取较为频繁,后者读取节拍慢一些,根据需要选择不同的方式,本例中我将使用Model类的tick成员函数读取AD采集值并显示到屏幕上。
37.jpg

比较偷懒的做法,初始化AD采集外设的代码可以放在main.c和main.h中,这样程序在系统初始化时就会自动初始化AD采集外设,并可以在程序的任何位置做AD采集操作:
  1. ADC_HandleTypeDef g_adc_handle;

  2. void ADC_PB0_Init(void)
  3. {

  4. __HAL_RCC_GPIOB_CLK_ENABLE();
  5. __HAL_RCC_ADC12_CLK_ENABLE();

  6. RCC_PeriphCLKInitTypeDef rcc_periph_clk_init_struct = {0};
  7. GPIO_InitTypeDef gpio_init_struct = {0};

  8. rcc_periph_clk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  9. rcc_periph_clk_init_struct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2P;
  10. HAL_RCCEx_PeriphCLKConfig(&rcc_periph_clk_init_struct);

  11. gpio_init_struct.Mode = GPIO_MODE_ANALOG;
  12. gpio_init_struct.Pull = GPIO_NOPULL;
  13. gpio_init_struct.Pin = GPIO_PIN_0;
  14. HAL_GPIO_Init(GPIOB , &gpio_init_struct);

  15. g_adc_handle.Instance = ADC1;
  16. g_adc_handle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV8;
  17. g_adc_handle.Init.Resolution = ADC_RESOLUTION_12B;
  18. g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  19. g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;
  20. g_adc_handle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  21. g_adc_handle.Init.LowPowerAutoWait = DISABLE;
  22. g_adc_handle.Init.ContinuousConvMode = DISABLE;
  23. g_adc_handle.Init.NbrOfConversion = 1;
  24. g_adc_handle.Init.DiscontinuousConvMode = DISABLE;
  25. g_adc_handle.Init.NbrOfDiscConversion = 1;
  26. g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  27. g_adc_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  28. g_adc_handle.Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL;
  29. g_adc_handle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
  30. g_adc_handle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  31. g_adc_handle.Init.OversamplingMode = DISABLE;
  32. HAL_ADC_Init(&g_adc_handle);

  33. HAL_ADCEx_Calibration_Start(&g_adc_handle, ADC_SINGLE_ENDED);
  34. }

  35. uint32_t adc_get_result(uint32_t channel)
  36. {
  37. ADC_ChannelConfTypeDef adc_channel_conf_struct = {0};
  38. uint32_t result;

  39. adc_channel_conf_struct.Channel = channel;
  40. adc_channel_conf_struct.Rank = ADC_REGULAR_RANK_1;
  41. adc_channel_conf_struct.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
  42. adc_channel_conf_struct.SingleDiff = ADC_SINGLE_ENDED;
  43. adc_channel_conf_struct.OffsetNumber = ADC_OFFSET_NONE;
  44. adc_channel_conf_struct.Offset = 0;
  45. adc_channel_conf_struct.OffsetSign = ADC_OFFSET_SIGN_NEGATIVE;
  46. HAL_ADC_ConfigChannel(&g_adc_handle, &adc_channel_conf_struct);

  47. HAL_ADC_Start(&g_adc_handle);
  48. HAL_ADC_PollForConversion(&g_adc_handle, HAL_MAX_DELAY);
  49. result = HAL_ADC_GetValue(&g_adc_handle);
  50. HAL_ADC_Stop(&g_adc_handle);

  51. return result;
  52. }

  53. uint32_t adc_get_result_average(uint32_t channel, uint8_t times)
  54. {
  55. uint32_t sum_result = 0;
  56. uint8_t index;
  57. uint32_t result;

  58. for (index=0; index<times; index++)
  59. {
  60.     sum_result += adc_get_result(channel);
  61. }

  62. result = sum_result / times;

  63. return result;
  64. }
复制代码
38.JPG 39.JPG

然后再Model.cpp的tick成员函数里做AD采集(实际上在Screen1View.cpp的tick回调事件函数里面做也是可以的,就是采集间隔会大很多):
40.JPG 41.JPG

然后最关键的来了,涉及到C++基础知识,多态性与虚函数,需要在ModelListener类里面注册一个虚函数的实现,随便起个名就可以,我起名为Notify_ADC_Value_Changed:
42.JPG

为什么要在ModelListener类里面注册虚函数的实现呢,因为ModelListener类是Screen1Presenter类的父类,是Screen1Presenter类是继承而来的,因此Screen1Presenter类也要注册Notify_ADC_Value_Changed函数,在Screen1Presenter类中的Notify_ADC_Value_Changed函数就要实现具体功能了,也就是将Model类对象的值赋给Screen1View类对象的值:
43.JPG 45.JPG 46.JPG

同时ModelListener类对象又是Model类的成员,Model类有个公开成员叫adc_pb0_value,可以由别的类进行调用,Screen1View类也有同名的公开成员adc_pb0_value,两者可以相互赋值:
44.JPG

在多态性与虚函数知识点中,可以得知,父类的同一个虚函数可以由不同子类继承并实现不同功能,以实现多态。

最后在Screen1View类的Tick事件回调函数中调用Notify_ADC_Value_Changed函进行上述操作,在Screen1View类的adc_pb0_value成员获得值并显示,完成了一次从Model->Present->View的信息传递,实现了将硬件外设的值传递给图形界面。
47.JPG

然后是通过界面控制硬件,这个就简单多了,直接在TouchGFX里面创建两个开关按钮,分别命名为Tb_led1 Tb_led2,写两个不同的事件回调函数即可:
48.JPG 49.JPG

看看效果,两个按钮分别控制绿灯和红灯的开关:
IMG_20250521_235717.jpg IMG_20250521_235719.jpg IMG_20250521_235722.jpg IMG_20250521_235726.jpg

旋转电位器旋钮改变AD输出值:
IMG_20250521_235804.jpg IMG_20250521_235806.jpg IMG_20250521_235808.jpg
收藏 评论0 发布时间:2025-5-22 00:22

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版