# Raspberry Pi Pico 学习 **Repository Path**: lceda/TEST_PICO ## Basic Information - **Project Name**: Raspberry Pi Pico 学习 - **Description**: 嵌入式智能编程综合实验——Arduino菜单 - **Primary Language**: C - **License**: LGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-06-02 - **Last Updated**: 2024-06-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: Arduino ## README

嵌入式智能编程综合实验——Arduino菜单

吕亮 2021210893
## 需求分析及功能描述 ### 需求分析 - 多个程序无法在一次烧录中同时实现 - 需要一个菜单,实现程序之间的切换 ### 主程序功能 - 实现了一个图形界面菜单,做到了多个子程序的选择运行(见附件 菜单.mp4)。 ![20240614_165501](assets/20240614_165501.jpg) ### 子程序功能 - 图片显示 实现了图片显示的功能,同时在菜单页面显示缩略图。 本工程中,图片显示子程序位于第0、1、3、4个页面。 - 示波器:改变纵轴分辨率和偏置 在例程`tftplot_QMI8658_gfx.cpp`的基础上,添加了使用按键改变纵轴分辨率和偏置的功能(见附件 示波器.mp4)。 ![20240614_165447](assets/20240614_165447.jpg) 本工程中,示波器子程序位于第2个页面。 - 球:根据光线改变颜色 在例程`ball_st7789_rp2040_wire1.cpp`的基础上,添加了光敏电阻外设,可以根据光线改变球的颜色。 本工程中,球子程序位于第5个页面。 ## 技术实现要点 - 设计页面切换逻辑 - 设计菜单页面服务程序,方便用户在不同程序之间来回切换。 - 设计子程序页面服务程序,定义代码规范,保证能实现任意程序的移植。 ## 主要程序流程图 ### setup() 在setup()中,初始化按键、TFT、子程序所需外设。 ![main-ver0.1-setup](assets/main-ver0.1-setup.png) ### loop() 在loop()中,首先轮询按键,然后执行菜单/子程序页面服务函数。 ![main-ver0.1-loop](assets/main-ver0.1-loop.png) ### 菜单页面服务程序 菜单页面用到了5个按键(上、下、左、右、中)来控制子页面的选中。 - 如果按下了上、下、左、右按键,那么改变行数或列数,并更新方框。 - 如果按下了中键,那么根据行数和列数切换到子程序页面。 ![main-ver0.1-handle_page_main](assets/main-ver0.1-handle_page_main.png) ### 子程序页面服务程序 子页面只需要1个按键(key6)作为返回菜单的按键。 - 如果按下了key6,那么切换到菜单页面。 - 具体实现的功能由子程序定义。 ![main-ver0.1-handle_pages[page]](assets/main-ver0.1-handle_pages[page].png) ## 主要代码 ### loop()中页面切换的逻辑 ```cpp // 使用列表和函数指针,简化代码 typedef int8_t (*func_ptr)(bool init); func_ptr handle_pages[9] = { handle_page_0, handle_page_1, handle_page_2, handle_page_3, handle_page_4, handle_page_5, handle_page_6, handle_page_7, handle_page_8}; void loop() { static int8_t page_prev = 0x7f; static int8_t page = -1; static bool page_init_flag = 1; // 扫描按键 checkButton(); // 页面切换逻辑 page_init_flag = (page != page_prev); page_prev = page; if (0 <= page && page <= 8) { page = handle_pages[page](page_init_flag); } else if (page == PAGE_MAIN) { page = handle_page_main(page_init_flag); } else { page = PAGE_MAIN; } } ``` ### 将任意程序移植到子程序页面 通过一定的代码规范,就可以实现在子程序页面运行任意程序(不能使用 key6)。 例如,要将一段Arduino程序移植到第6个页面: ```cpp int8_t handle_page_6(bool init) // PIC_STM32 { static int8_t page_nxt = 6; // Global variables // ... if (init) { // setup // ... page_nxt = 6; } // loop // ... if (buttonUprise.key6) { page_nxt = PAGE_MAIN; } return page_nxt; } ``` 将原程序的全局变量复制到// Global variables,setup()函数复制到// setup,loop()函数复制到// loop,就可以实现原程序在子程序页面中的运行。 根据主程序loop()函数的执行逻辑,// setup中的代码在每次切换到该页面时被执行一次,// loop中的代码在停留在该页面时被重复执行,从而和原程序的运行顺序保持一致。 ### 用轮询定时器代替delay() 在例程`tftplot_QMI8658_gfx.cpp`和`ball_st7789_rp2040_wire1.cpp`中,都使用了软件延时delay()来控制采样间隔或刷新频率。本项目提出了一种改进思路,使用轮询定时器代替软件延时,增加了定时间隔确定性,减少了软件阻塞。 代码如下: ```cpp // loop timerval = millis(); // 轮询定时器,伪定时器中断 if (timerval - timerval_prev >= 10) { timerval_prev = timerval; // 10ms执行一次的程序 } ``` 代码分析: 通过 millis() 函数读取内部定时器的毫秒值(timerval),如果读取到的毫秒值(timerval) - 上一次执行程序的毫秒值(timerval_prev) > 10,则执行需要定时的程序,否则就不执行。 由于没有找到适用于 Pico 的 Arduino 定时器中断库,因此只能用这种方法代替。 ## 结果及心得 ### 实验结果 实现了菜单的主要功能,并基于例程实现了功能拓展。 ### 可以改进之处 1. 当有多个子程序争用同一资源(如QMI8658或其他外设)时,移植的过程可能还需要优化以达到最佳效果。可以使用前后台分离的思想来实现。 2. 程序移植的通用性还有待验证。目前只移植了两个例程,其他更复杂的程序在移植过程中还可能产生新的问题。 3. 如果要改为多级菜单,可能需要进一步规划数据结构。 ## 所有代码 [https://gitee.com/lceda/TEST_PICO/tree/master](https://gitee.com/lceda/TEST_PICO/tree/master)