【OLED多级菜单——树结构】

一、硬件构成

1、工具材料

废话少说,一般有两种方法实现OLED多级菜单。1、链表。2、树。
本文主要介绍树结构实现的OLED多家菜单。下面来看硬件搭配

  • STM32F103C8T6核心板
  • 1.3寸SPI OLED屏幕
  • EC11旋转编码器
  • 串口模块以及Stlink调试工具

2、硬件连接

  1. OLED屏幕———————>B12-B15

  2. EC11旋转编码器————>A8、A9以及按键引脚A10

二、代码结构

1、前提要求时下面三个模块可以正常工作:

  1. 编码器驱动
  2. OLED驱动(密码:w0p8)
  3. 串口重定向输出
  4. 如果上面的三个没有做好,可以看我的其他文章

1、main.c

int main(void)
{
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();

  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
	
  OLED_Init();//OLED显示屏初始化引脚函数
  OLED_ColorTurn(0);//0正常显示,1 反色显示
  OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
  
	menu_init();//菜单初始化
  while (1)
  {
  }
}

2、gpio.c

#include "gpio.h"

/* USER CODE BEGIN 0 */
#include "usart.h"
#include "Tree_OLED.h"
/* USER CODE END 0 */

/*----------------------------------------------------------------------------*/
/* Configure GPIO                                                             */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/** Configure pins as
        * Analog
        * Input
        * Output
        * EVENT_OUT
        * EXTI
*/
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
	__HAL_RCC_GPIOC_CLK_ENABLE();
	
	HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = EC11_SW_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(EC11_SW_GPIO_Port, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 3, 0);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

}

/* USER CODE BEGIN 2 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  if(GPIO_Pin==EC11_SW_Pin)
	{
		menu_item_t *item;
		HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
		item=get_menu_item_child(current,NUM);//获取到current的子菜单数组的第NUM个菜单项
		item->function();//调用菜单项的回调函数
	}
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}

3、usart.c

void u1_printf(char *fmt,...){//串口重定向输出
	uint8_t tempbuff[256];
	uint16_t i;
	va_list ap;//相当于定义的参数列表
	va_start(ap,fmt);
	vsprintf((char *)tempbuff,fmt,ap);//把ap以fmt的格式放入到tempbuff中
	va_end(ap);
	
	for(i=0;i<strlen((char *)tempbuff);i++){
		while(!__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE));
		huart1.Instance->DR = tempbuff[i];
	}
	while(!__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC));
}

4、Tree_OLED.h中菜单的数据结构

extern int NUM;

#define MAX_MENU_ITEMS 4
typedef struct menu_item {
    char name[10];//项的姓名 
    struct menu_item *parent;//父节点 
    struct menu_item *children[MAX_MENU_ITEMS];//子节点数组 
    uint8_t child_count;//孩子节点数量
	void (*function)(void);//回调函数
	
} menu_item_t;

typedef struct menu {
    menu_item_t *root;
    uint8_t item_count;
} menu_t;

5、部分Tree_OLED.c函数

//调用退出菜单回调函数
void EXIT_Function(void){
	current=current->parent;
	NUM=0;//使其显示子菜单时显示第一个选项
	Show_Menu();
}
void show_OLED_Menu(void){//旋转编码器旋转时在中断执行的函数
	Show_Menu();
}
void EC11_SW_DOWN(void){//节点的显示回调函数
	current=get_menu_item_child(current,NUM);
	NUM=0;//使其显示子菜单时显示第一个选项
	Show_Menu();
}
void Show_Menu(void){//显示当前菜单的子菜单项
	uint8_t i=0;
	OLED_Clear();//清屏
	for(i=0;i<current->child_count;i++)
	{
		if(i==NUM)
		{
			//u1_printf("%s\r\n","MY");
			OLED_ShowString(0,i*18,(uint8_t *)get_menu_item_child(current,i),16,0);
		}
		else
		{
			//u1_printf("%s\r\n",(char *)get_menu_item_child(current,i));
			OLED_ShowString(0,i*18,(uint8_t *)get_menu_item_child(current,i),16,1);
		}
		
	}
	OLED_Refresh();
}
void menu_init(void)//菜单初始话函数,可以自己添加 
{
	menu_item_t *root   = create_menu_item((char *)"ROOT",  NULL,  NULL);//创建节点,
	menu_item_t *child1 = create_menu_item((char *)"child1", root,  EC11_SW_DOWN);
	menu_item_t *child2 = create_menu_item((char *)"child2", root,  EC11_SW_DOWN);
	menu_item_t *child3 = create_menu_item((char *)"child3", root,  EC11_SW_DOWN); 
	
	menu_item_t *child4 = create_menu_item((char *)"child4", child1,  EC11_SW_DOWN);//添加回调函数
	menu_item_t *child5 = create_menu_item((char *)"child5", child1,  EC11_SW_DOWN);
	menu_item_t *child6 = create_menu_item((char *)"EXIT", child1,  EXIT_Function);
//	
	menu_item_t *child7 = create_menu_item((char *)"child7", child2,  EC11_SW_DOWN);
	menu_item_t *child8 = create_menu_item((char *)"EXIT", child2,  EXIT_Function);
//	menu_item_t *child9 = create_menu_item((char *)"子菜单项9", child2,  NULL);
	
	add_menu_item(root, child1);//添加加节点到树结构中
	add_menu_item(root, child2);
	add_menu_item(root, child3);
	
	add_menu_item(child1, child4);
	add_menu_item(child1, child5);
	add_menu_item(child1, child6);
	
	add_menu_item(child2, child7);
	add_menu_item(child2, child8);
//	add_menu_item(child2, child9);
	//创建菜单
	menu_t *menu = create_menu(root);//创建菜单(唯一)
	//当前节点是根节点
	current=root;

}

5、tim.c中的定时器中断回调函数

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
  if(htim->Instance==TIM1)//判断是否是定时器1
	{
		if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)//判断是否是通道1触发
		{
			if(__HAL_TIM_IS_TIM_COUNTING_DOWN(htim))//判断DIR,计数方向向下
			{
				count=((int16_t)HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)-1)/4;//根据CNT计数器的值少计数1,所以加一
				NUM=((abs(count)%get_menu_item_child_count(current)+1)-1);//把NUM的值控制到该子菜单的数量
			}
		}
		else if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2)
		{
			if(!__HAL_TIM_IS_TIM_COUNTING_DOWN(htim))
			{
				count=((int16_t)HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2)+1)/4;
				NUM=((abs(count)%get_menu_item_child_count(current)+1)-1);
			}
		}
		show_OLED_Menu();//每次转动旋转编码器都更新屏幕内容
	}
}

三、效果

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值