开发要求
a. LCD上实现按键点灯,按键和灯均为LCD虚拟出来的
开发过程
1.配置环境
1.1安装Cubemx ,如果有就跳过这一步
下载Cube MX 5.0 ,下载地址https://www.st.com/en/developmenttools/stm32cubemx.html
下载步骤略
2.配置工程
从菜单栏 help 进入 Manage embedded software packages 界面,点击 From Url 按钮,进入 User Defined Packs Manager 界面,其次点击 new,填入上述网址,然后点击 check,如下图所示:
check 通过后,点击 OK 回到 User Defined Packs Manager 界面,再次点击 OK,CubeMX 自动连接服务器,获取包描述文件。回到 Manage embedded software packages 界面,就会发现 RT-Thread Nano 3.1.3软件包,选择该软件包,点击 Install Now,如下图所示:
点击安装之后,弹出 Licensing Agreement ,同意协议,点击 Finish,
等待安装完成,成功安装后,版本前面的小蓝色框变成填充的黄绿色
在 CubeMX 主界面的菜单栏中 File 选择 New Project,如下图所示
新建工程之后,在弹出界面芯片型号中输入某一芯片型号,方便锁定查找需要的芯片,双击被选中的芯片
选中芯片型号之后,点击 Softwares Packages->Select Components,进入组件配置界面,选择 RealThread, 然后根据需求选择 RT-Thread 组件,然后点击 OK 按钮,如下图所示(和图片不一样的是要选3.1.3版本的,选3.1.5的无法配置nano):
选择组件之后,对组件参数进行配置。在工程界面 Pinout & Configuration 中,进入所选组件参数配置区,按照下图进行配置
2.2用Cubemx配置引脚
按截图的顺序去操作配置触摸要用的引脚及其模式等
之后配置FSMC,用于LCD 的显示等
下面的是与fsmc时序有关的配置,配完需要仔细检查有没有配错
配置一个串口,设为异步,用于调试
配置时钟
配置系统时钟,要挂载在定时器六下面,不然会与RT-Thread 的时钟冲突
【补充:关于NVIC的配置,注意:RT-Thread 操作系统重定义 HardFault_Handler、PendSV_Handler、SysTick_Handler 中断函数,为了避免重复定义的问题,在生成工程之前,需要在中断配置中,代码生成的选项中,取消选择三个中断函数(对应注释选项是 Hard fault interrupt, Pendable request, Time base :System tick timer)】
配置时钟树
之后生成代码
3.修改并完善工程
3.1用keil打开工程,编译运行发现有错误,此时需要修改:
- 修改文件类型 避免keil识别不出
- 注释掉shell文件里第182行处
extern char rt_hw_console_getchar(void);
return rt_hw_console_getchar();
之后再编译应该就不会出现报错了
由于要用到邮箱 我们要把下面红线处的给取消注释
3.3 移植要用到的触摸屏代码,涉及到LCD/TOUCH/SPI等文件的移植
复制网盘链接下载文件到工程(按理不会报错,已把移植后发生的报错解决好了)
通过百度网盘分享的文件:hw 2024-…
链接:https://pan.baidu.com/s/1QQZ2QgOGyeWK-eEwYS8AJw?pwd=3qqo
提取码:3qqo
复制这段内容打开「百度网盘APP 即可获取」
工程里新建一个light.h文件,复制以下代码到里面
#ifndef __LIGHT_H
#define __LIGHT_H
#include "sys.h"
#include "stdlib.h"
#include "lcd.h"
uint16_t gImage_light[xxx] = {};
#endif
由于uint16_t gImage_light[xxx] = {};里面包含的数据过长,此处建议用图片取模工具完成,图片取模工具的百度网盘链接:
密码是mg7k
去网上找一张灯的图片 导入取模工具里,设置成类似这样
完成设置,点击保存按钮,在弹出的对话框,输入想要保存的文件名,最好设为.h输出,之后复制到刚刚新建的light.h里
【参考csdn的这篇博客:https://blog.csdn.net/py8105/article/details/128184645?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170738143716800182116447%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170738143716800182116447&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-128184645-null-null.142^v99^pc_search_result_base6&utm_term=img2lcd&spm=1018.2226.3001.4187】
3.4 main.c文件里修改代码
由于我们是基于邮箱去传递信息,所以打开rtthread官网复制关于邮箱的相关例程到main.c文件里main函数之前
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2?id=%e9%82%ae%e7%ae%b1
例程如下:
#include <rtthread.h>
#define THREAD_PRIORITY 10
#define THREAD_TIMESLICE 5
/* 邮箱控制块 */
static struct rt_mailbox mb;
/* 用于放邮件的内存池 */
static char mb_pool[128];
static char mb_str1[] = "I'm a mail!";
static char mb_str2[] = "this is another mail!";
static char mb_str3[] = "over";
ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
/* 线程 1 入口 */
static void thread1_entry(void *parameter)
{
char *str;
while (1)
{
rt_kprintf("thread1: try to recv a mail\n");
/* 从邮箱中收取邮件 */
if (rt_mb_recv(&mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER) == RT_EOK)
{
rt_kprintf("thread1: get a mail from mailbox, the content:%s\n", str);
if (str == mb_str3)
break;
/* 延时 100ms */
rt_thread_mdelay(100);
}
}
/* 执行邮箱对象脱离 */
rt_mb_detach(&mb);
}
ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程 2 入口 */
static void thread2_entry(void *parameter)
{
rt_uint8_t count;
count = 0;
while (count < 10)
{
count ++;
if (count & 0x1)
{
/* 发送 mb_str1 地址到邮箱中 */
rt_mb_send(&mb, (rt_uint32_t)&mb_str1);
}
else
{
/* 发送 mb_str2 地址到邮箱中 */
rt_mb_send(&mb, (rt_uint32_t)&mb_str2);
}
/* 延时 200ms */
rt_thread_mdelay(200);
}
/* 发送邮件告诉线程 1,线程 2 已经运行结束 */
rt_mb_send(&mb, (rt_uint32_t)&mb_str3);
}
int mailbox_sample(void)
{
rt_err_t result;
/* 初始化一个 mailbox */
result = rt_mb_init(&mb,
"mbt", /* 名称是 mbt */
&mb_pool[0], /* 邮箱用到的内存池是 mb_pool */
sizeof(mb_pool) / 4, /* 邮箱中的邮件数目*/
RT_IPC_FLAG_FIFO); /* 采用 FIFO 方式进行线程等待 */
if (result != RT_EOK)
{
rt_kprintf("init mailbox failed.\n");
return -1;
}
rt_thread_init(&thread1,
"thread1",
thread1_entry,
RT_NULL,
&thread1_stack[0],
sizeof(thread1_stack),
THREAD_PRIORITY, THREAD_TIMESLICE);
rt_thread_startup(&thread1);
rt_thread_init(&thread2,
"thread2",
thread2_entry,
RT_NULL,
&thread2_stack[0],
sizeof(thread2_stack),
THREAD_PRIORITY, THREAD_TIMESLICE);
rt_thread_startup(&thread2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mailbox_sample, mailbox sample);
大体代码是差不多的,但需要更改一些细节
我们的思路是线程1用于接收线程2发送的邮件,触发线程2发邮件的事件是触摸屏被按下且按下的触摸屏的位置是我们设置的“开关”处(开关的实现是用LCD_Fill函数去控制一块地方呈现颜色)
①所以我们可以修改设置邮件内容为
rt_uint32_t mb_str1 = 1;//开灯
rt_uint32_t mb_str3 = 3;//结束
- 修改线程1,2的入口函数,其他的不用修改
ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
static void thread1_entry(void *parameter)
{
rt_uint32_t str;
rt_uint8_t i;
i = 0;
while (1)
{
/* 从邮箱中收取邮件 */
if (rt_mb_recv(&mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER) == RT_EOK)
{
if (str == mb_str3){
continue;
}
i ++;
LCD_Fill(200,270,230,310,GREEN);
if(i%2==1)
{
show_picture1(0,0,150,168,gImage_light);
rt_thread_mdelay(120);
}
if((i%2==0)){
LCD_Fill(0,0,150,168,WHITE);
rt_thread_mdelay(150);
}
rt_thread_mdelay(10);
}
}
/* 执行邮箱对象脱离 */
rt_mb_detach(&mb);
}
ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程 2 入口 */
static void thread2_entry(void *parameter)
{
LCD_Fill(200,270,230,310,RED);
while (1)
{
if (tp_dev.scan(0))
{ if(tp_dev.x[0]>200&&tp_dev.y[0]>270&&tp_dev.x[0]<230&&tp_dev.y[0]<310){
/* 发送 mb_str1 地址到邮箱中 */
rt_mb_send(&mb, (rt_uint32_t)mb_str1);
}
}
else
{
/* 发送 mb_str2 地址到邮箱中 */
rt_mb_send(&mb, (rt_uint32_t)mb_str3);
}
rt_thread_mdelay(300);
}
/* 发送邮件告诉线程 1,线程 2 已经运行结束 */
//rt_mb_send(&mb, (rt_uint32_t)mb_str3);
}
一般而言不会报错,报错的原因多由于头文件没有加入
之后把main函数改成这样
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_FSMC_Init();
/* USER CODE BEGIN 2 */
//HAL_DeInit();
LCD_Init(); //LCD初始化
rt_thread_mdelay(1000); // delay 50 ms
W25QXX_Init();
POINT_COLOR=RED;
rt_thread_mdelay(100);
tp_dev.init();
rt_thread_mdelay(700);
TP_Adjust();
rt_thread_mdelay(700);
LCD_Clear(WHITE);//清除屏幕
mailbox_sample();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
rt_thread_mdelay(1300);
}
/* USER CODE END 3 */
}