cubemx stm32 按键key 支持单、双、三、四击以及长按 链表实现 驱动代码

20 篇文章 2 订阅
15 篇文章 0 订阅

cubemx配置

  1. 打开串口
  2. 配置按键的 gpio口为 up input 模式
  3. 配置时钟树

代码

yxy_open_key.c

#include "yxy_open_key.h"
#include "stdlib.h"
#include "string.h"
#include "yxy_debug.h"



/* // 按键实现思路:
	
	用户需要使用某个按键的时候,
	把按键通过 key_add 添加到链表里面,
	key_add函数会把按键名称绑定到对应的按键端口和按键引脚上面去,
	当按键按下时,会在 key_scan 函数中跳转到对应的按键处理函数中去,
	并且如果 KEY_UART_DEBUG 为1 的话,
	则会把按键名称以及对应的按键方式打印到串口上面去。
	
	概念:
		时间基段:判断按键的击打状态的时间基准。
		
		单击判断条件:	按下时间在第一个时间基段以内,
						并且按下结束后在一个时间基段内没有按下的算单击。
		长按判断条件:	按下时间超过 4 个时间基段的则可以认为是长按
		
		双击判断条件:	第一次的按下时间在一个时间基段以内,
						第一次和第二次按下之间的时间不能大于一个时间基段,
						第二次无论按下多长,都算是双击,
						第二次按下结束后在一个时间基段内没有按下,则是双击。
	
		三击判断条件:	三击和双击的原理一样。
		四击判断条件:	四击和双击的原理一样。
	
	使用:
	
		创建一个 User_Key userVar 全局变量
		
		初始化:
			openKey_init();
			key_add(&userVar, (uint8_t *)"keyName", KEY_GPIO_Port, KEY_Pin);
			
		main主循环:
			while(1)
			{
				// 每1ms扫描一次 key_scan 函数(在while中用任意代码使他等待1ms)。
				key_scan();
				HAL_Delay(1);
			}
			
			重写回调函数,key_click、key_double、key_third、key_fourth、key_long
			在回调函数中if(key_id == userVar.keyID),判断是哪个按键的回调函数,
			并在对应的回调中写对应的用户代码。


	
*/

/*********************
 *    宏定义区域
 *********************/




/*********************
 *   内部变量区域
 *********************/

uint16_t keyNum = 0;	// 按键数量

struct Key_Link * p_kl = NULL;



/*********************
 *    回调弱函数
 *********************/

/* key中调函数(调用回调函数之前先调用中调函数)
 * 用来确认调用哪个回调函数
 */


__weak void key_click(uint16_t key_id)
{
	USER_PASS;
}


#if KEY_SUPPORT >= 2
__weak void key_double(uint16_t key_id)
{
	USER_PASS;
}

#endif
#if KEY_SUPPORT >= 3
__weak void key_third(uint16_t key_id)
{
	USER_PASS;
}

#endif
#if KEY_SUPPORT >= 4
__weak void key_fourth(uint16_t key_id)
{
	USER_PASS;
}

#endif

__weak void key_long(uint16_t key_id)
{
	USER_PASS;
}

/*********************
 *     内部函数
 *********************/

static uint16_t Radical_2(uint16_t x);

/* 连续开 2 次方根,计算某个数是2的多少次方跟 */
static uint16_t Radical_2(uint16_t x)
{
	uint16_t y = 0;
	
	while(x)
	{
		x >>= 1;
		y++;
	}
	y--;
	
	return y;
}


/*********************
 *   外部函数实现
 *********************/

/**
  * @brief  开源按键的初始化
  * @param  
  * @retval 返回 HAL_OK 则初始化成功
  */
HAL_StatusTypeDef openKey_init(void)
{
	p_kl = (struct Key_Link *)malloc(sizeof(struct Key_Link));
	
	if(p_kl == NULL)
	{
		yxy_DEBUG("open key initialization failed... \r\n");
		return HAL_ERROR;
	}
	
	p_kl->p_kN = NULL;
	strcpy((char *)p_kl->kC.keyName, "keyLinkHand");	//按键名字为"keyLinkHand"
	p_kl->kC.keyID = 0xffff;		// 给头结点赋值一个到不了的值
	
	p_kl->kC.keyDownCnt = 0;		// 按键按下计数为0
	p_kl->kC.keyUpCnt = 0;			// 按键抬起计数为0
	p_kl->kC.keyManyCliCnt = 0;		// 按键多击计数为0
	p_kl->kC.kDownLockState = KEY_NULL;// 当前无按下的解锁状态

//	yxy_DEBUG("%s\r\n", p_kl->kC.keyName);
	
	// 按键数量为零
	keyNum = 0;
	
	yxy_DEBUG("open key initialization success !\r\n");
	
	return HAL_OK;
}

/**
  * @brief  添加一个按键到按键链表里面
  * @param  按键的名字
  * @param  按键的端口
  * @param  按键的引脚
  * @retval 返回 HAL_OK 则添加按键成功
  */
HAL_StatusTypeDef key_add(User_Key * userKey, uint8_t * keyName, GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
	struct Key_Link * p_keyLinkNode = p_kl;	// 创建一个中间变量保存p_kl的值
	uint8_t GPIOxName = (uint8_t)((((uint32_t)GPIOx - (uint32_t)GPIOA) / 0x00000400UL) + 'A');
	uint16_t PinName = Radical_2(GPIO_Pin);
	
	if(IS_GPIO_ALL_INSTANCE(GPIOx) != 1)
	{
		yxy_DEBUG("key add GPIOx error... \r\n");
		return HAL_ERROR;
	}

	if((IS_GPIO_PIN(GPIO_Pin) != 1) && (GPIO_Pin != GPIO_PIN_All))
	{
		yxy_DEBUG("key add GPIO_Pin error... \r\n");
		return HAL_ERROR;
	}
	
	// 找到链表的尾部分
	while(p_keyLinkNode->p_kN != NULL)
	{
		p_keyLinkNode = p_keyLinkNode->p_kN;
	}
	
	// 给新的按键创建一个空间
	p_keyLinkNode->p_kN = (struct Key_Link *)malloc(sizeof(struct Key_Link));
	
	p_keyLinkNode = p_keyLinkNode->p_kN;
	
	if(p_keyLinkNode == NULL)
	{
		yxy_DEBUG("key add failed... \r\n");
		return HAL_ERROR;
	}
	
	p_keyLinkNode->p_kN = NULL;

	// 按键名字赋值
	strcpy((char *)p_keyLinkNode->kC.keyName, (const char *)keyName);
	
	// 按键id随着按键的增加逐渐递加
	p_keyLinkNode->kC.keyID = keyNum;
	
	// 按键数量加1
	keyNum++;
	
	// 按键绑定硬件端口、引脚
	p_keyLinkNode->kC.GPIOx = GPIOx;
	p_keyLinkNode->kC.GPIO_Pin = GPIO_Pin;
	
	// 状态归位
	p_keyLinkNode->kC.keyDownCnt = 0;
	p_keyLinkNode->kC.keyUpCnt = 0;
	p_keyLinkNode->kC.keyManyCliCnt = 0;
	p_keyLinkNode->kC.kDownLockState = KEY_NULL;
	
	// 把初始化好的 key 给用户用
	userKey->GPIOx = GPIOx;
	userKey->GPIO_Pin = GPIO_Pin;
	userKey->keyID = p_keyLinkNode->kC.keyID;
	userKey->portname = GPIOxName;
	userKey->pinName = PinName;

	// 打印添加的按键信息
	yxy_DEBUG("New key :\r\n   name : \"%s\" \r\n", p_keyLinkNode->kC.keyName);
	yxy_DEBUG("   port : GPIO%c\r\n", GPIOxName);
	yxy_DEBUG("   Pin  : PIN_%d\r\n", PinName);
	yxy_DEBUG("\r\n");
	
	return HAL_OK;
}

/**
  * @brief  按键链表删除一个按键节点
  * @param  按键的端口
  * @param  按键的引脚
  * @retval 返回 HAL_OK 则添加按键成功
  */
HAL_StatusTypeDef key_clear(User_Key * userKey)
{
	struct Key_Link * p_keyLinkNode = p_kl;	// 创建一个中间变量保存p_kl的值
	struct Key_Link * p_keyNode_temp = NULL;
	
	if(IS_GPIO_ALL_INSTANCE(userKey->GPIOx) != 1)
	{
		yxy_DEBUG("key clear GPIOx error... \r\n");
		return HAL_ERROR;
	}

	if(IS_GPIO_PIN(userKey->GPIO_Pin) != 1)
	{
		yxy_DEBUG("key clear GPIO_Pin error... \r\n");
		return HAL_ERROR;
	}
	
	// 在整个链表寻找按键
	while(p_keyLinkNode->p_kN != NULL)
	{
		// 寻找对应的按键,一但下一个节点是对应的按键,则把当前的节点连接到后面的节点上,并删除按键节点
		if((p_keyLinkNode->p_kN->kC.GPIOx == userKey->GPIOx) && (p_keyLinkNode->p_kN->kC.GPIO_Pin == userKey->GPIO_Pin))
		{
			p_keyNode_temp = p_keyLinkNode->p_kN;
			p_keyLinkNode->p_kN = p_keyNode_temp->p_kN;
			
			free(p_keyNode_temp);
			p_keyNode_temp = NULL;
			
			userKey->GPIOx = NULL;
			userKey->GPIO_Pin = 0;
			userKey->keyID = 0xff;
			
			yxy_DEBUG("key clear ok!\r\n");
			return HAL_OK;
		}
		
		p_keyLinkNode = p_keyLinkNode->p_kN;
	}
	
	return HAL_ERROR;
}
	
/**
  * @brief  扫描按键的状态,并跳转到对应的按键处理函数中
  * @param  
  * @retval 
  */
void key_scan(void)
{
	struct Key_Link * p_keyLinkNode = p_kl->p_kN;	// 创建一个中间变量保存p_kl的值

	// 扫描每一个节点看看,有没有按键的状态发生变化
	while(p_keyLinkNode != NULL)
	{
		// 若是当前节点的按键按下了
		if((p_keyLinkNode->kC.GPIOx->IDR & p_keyLinkNode->kC.GPIO_Pin) == (uint32_t)GPIO_PIN_RESET) // 按下不一定是低电平,后期可以适配
		{
			p_keyLinkNode->kC.keyUpCnt = 0;
			p_keyLinkNode->kC.keyDownCnt++;
			
			switch(p_keyLinkNode->kC.kDownLockState)
			{
				case KEY_NULL:
				{
					// 消抖处理
					if(p_keyLinkNode->kC.keyDownCnt >= KEY_Jitters_Elimination)
					{
						p_keyLinkNode->kC.keyDownCnt = 0;
						p_keyLinkNode->kC.keyManyCliCnt = 0;
						
						// 中间状态晋升为单击
						p_keyLinkNode->kC.kDownLockState = KEY_CLICK;
					}
				}
				break;
				case KEY_CLICK:
				{
					// 消抖处理
					if((p_keyLinkNode->kC.keyDownCnt >= KEY_Jitters_Elimination) && (p_keyLinkNode->kC.keyManyCliCnt == 1))
					{
						p_keyLinkNode->kC.keyDownCnt = 0;
						p_keyLinkNode->kC.keyManyCliCnt = 0;
						
#if KEY_SUPPORT >= 2
						// 中间状态晋升为双击
						p_keyLinkNode->kC.kDownLockState = KEY_DOUBLE;
#else
						// 无法判断为双击,则执行单击
						p_keyLinkNode->kC.kDownLockState = KEY_NULL;
						key_click(p_keyLinkNode->kC.keyID);
#endif
					}
					
					// 当预判断为单击的时候,仍按下2秒就是长按
					if((p_keyLinkNode->kC.keyDownCnt >= TIME_BASE_SLOT * 4) && (p_keyLinkNode->kC.keyManyCliCnt < 1))
					{
						p_keyLinkNode->kC.keyDownCnt = 0;
						p_keyLinkNode->kC.kDownLockState = KEY_NULL;
						
						key_long(p_keyLinkNode->kC.keyID);
						
						// 等待长按结束
						while((p_keyLinkNode->kC.GPIOx->IDR & p_keyLinkNode->kC.GPIO_Pin) == (uint32_t)GPIO_PIN_RESET);
					}
				}
				break;
#if KEY_SUPPORT >= 2
				case KEY_DOUBLE:
				{
					// 消抖处理
					if((p_keyLinkNode->kC.keyDownCnt >= KEY_Jitters_Elimination) && (p_keyLinkNode->kC.keyManyCliCnt == 2))
					{
						p_keyLinkNode->kC.keyDownCnt = 0;
						p_keyLinkNode->kC.keyManyCliCnt = 0;
						
#if KEY_SUPPORT >= 3
						// 中间状态晋升为三击
						p_keyLinkNode->kC.kDownLockState = KEY_THIRD;
#else
						// 无法判断为三击,则执行双击
						p_keyLinkNode->kC.kDownLockState = KEY_NULL;
						key_double(p_keyLinkNode->kC.keyID);
#endif
					}
				}
				break;
#endif
#if KEY_SUPPORT >= 3
				case KEY_THIRD:
				{
					// 消抖处理
					if((p_keyLinkNode->kC.keyDownCnt >= KEY_Jitters_Elimination) && (p_keyLinkNode->kC.keyManyCliCnt == 3))
					{
						p_keyLinkNode->kC.keyDownCnt = 0;
						p_keyLinkNode->kC.keyManyCliCnt = 0;
						
#if KEY_SUPPORT >= 4
						// 中间状态晋升为四击
						p_keyLinkNode->kC.kDownLockState = KEY_FOURTH;
#else
						// 无法判断为四击,则执行三击
						p_keyLinkNode->kC.kDownLockState = KEY_NULL;
						key_third(p_keyLinkNode->kC.keyID);
#endif
					}
				}
				break;
#endif
#if KEY_SUPPORT >= 4
				case KEY_FOURTH:
				{
					
				}
				break;
#endif		
				default:
					
				break;
			}
		}
		else // 若是当前节点的按键没按下
		{
			if(p_keyLinkNode->kC.kDownLockState != NULL)
			{
				p_keyLinkNode->kC.keyDownCnt = 0;
				p_keyLinkNode->kC.keyUpCnt++;
				
				switch(p_keyLinkNode->kC.kDownLockState)
				{
					case KEY_CLICK:
					{
						// 击打次数为 1
						p_keyLinkNode->kC.keyManyCliCnt = 1;
					}
					break;
#if KEY_SUPPORT >= 2				
					case KEY_DOUBLE:
					{
						// 击打次数为 2
						p_keyLinkNode->kC.keyManyCliCnt = 2;
					}
					break;
#endif
#if KEY_SUPPORT >= 3
					case KEY_THIRD:
					{
						// 击打次数为 3
						p_keyLinkNode->kC.keyManyCliCnt = 3;
					}
					break;
#endif
#if KEY_SUPPORT >= 4
					case KEY_FOURTH:
					{
						// 击打次数为 4
						p_keyLinkNode->kC.keyManyCliCnt = 4;
					}
					break;
#endif
					default:
					break;
				}
				
				if(p_keyLinkNode->kC.keyUpCnt >= TIME_BASE_SLOT)
				{
					p_keyLinkNode->kC.keyUpCnt = 0;
					p_keyLinkNode->kC.keyManyCliCnt = 0;
					
					switch(p_keyLinkNode->kC.kDownLockState)
					{
						case KEY_CLICK:
							key_click(p_keyLinkNode->kC.keyID);
							break;
#if KEY_SUPPORT >= 2
						case KEY_DOUBLE:
							key_double(p_keyLinkNode->kC.keyID);
							break;
#endif
#if KEY_SUPPORT >= 3
						case KEY_THIRD:
							key_third(p_keyLinkNode->kC.keyID);
							break;
#endif
#if KEY_SUPPORT >= 4
						case KEY_FOURTH:
							key_fourth(p_keyLinkNode->kC.keyID);
							break;
#endif
						default:
							break;
					}
					p_keyLinkNode->kC.kDownLockState = KEY_NULL;
				}
			}
		}
		
		p_keyLinkNode = p_keyLinkNode->p_kN;
	}
}

/**
  * @brief  获取已经添加的按键数量
  * @retval 返回添加的按键数量
  */
uint16_t get_key_Num(void)
{
	return keyNum;
}


/**
  * @brief  获取按键绑定的名字
  * @param  获取按键名字的buff(buff大小至少需要20个字节)
  * @param  按键的端口
  * @param  按键的引脚号
  * @retval 无
  */
HAL_StatusTypeDef get_key_Name(User_Key * userKey, uint8_t * nameReadBuff)
{
	struct Key_Link * p_keyLinkNode = p_kl->p_kN;	// 创建一个中间变量保存p_kl的值
	
	while(p_keyLinkNode != NULL)
	{
		// 寻找对应的按键
		if((p_keyLinkNode->kC.GPIOx == userKey->GPIOx) && (p_keyLinkNode->kC.GPIO_Pin == userKey->GPIO_Pin))
		{
			strcpy((char *)nameReadBuff, (const char *)p_keyLinkNode->kC.keyName);
			return HAL_OK;
		}
		
		p_keyLinkNode = p_keyLinkNode->p_kN;
	}
	
	return HAL_ERROR;
}



yxy_open_key.h

#ifndef __KEY_H_
#define __KEY_H_

/* 支持C++调用。
 * 用extern"C"来告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们。
 */
#ifdef __cplusplus
extern "C" {
#endif

/*********************
 *      INCLUDES
 *********************/
 
#include "gpio.h"

/*Error checking*/
#ifdef YXY
//	#error "故意给你一个错误,啦啦啦!"
	#warning "这个警告要害死强迫症!"
#endif



/*********************
 *      DEFINES
 *********************/


/* 按键多击支持(默认支持单击和长按)
 *
 * >=2 则可支持2击
 * >=3 则可支持2、3击
 * >=4 则可支持2、3、4击
*/
#define KEY_SUPPORT 4

/* 最大支持的按键数量 */
#define KEY_MAX_NUM 10

/* 默认按键名字最大长度,默认值为20 */
#define KEY_NUME_LEN_MAX 20

/* 消抖时间 - 20ms */
#define KEY_Jitters_Elimination 20

/* 时间基段 - 100ms */
#define TIME_BASE_SLOT 100



/**********************
 *      TYPEDEFS
 **********************/
 
 
/* 按键状态值KEY_枚举 */
typedef enum
{
	KEY_NULL = 0	, // 按键无动作
	
	KEY_CLICK = 1	, // 默认支持单击
	
#if KEY_SUPPORT >= 2
	KEY_DOUBLE = 2	,
#endif
#if KEY_SUPPORT >= 3
	KEY_THIRD  = 3	,
#endif
#if KEY_SUPPORT >= 4
	KEY_FOURTH  = 4	,
#endif
	
	KEY_MAX_VAL = 0xffff, // 按键状态最大值(永远无法超越的值)
	
	KEY_LONG_PRESS = KEY_MAX_VAL-1, // 长按
	
	KEY_LAST_NULL_VAL = 0xffff // 按键最后的无效值
}KEY_STATE;


/*  按键类结构体 */
typedef struct
{
	/* 按键的名称、代号 */
	uint8_t keyName[KEY_NUME_LEN_MAX];	// 按键名字,长度可由 KEY_NUME_LEN 设置
	uint16_t keyID;						// 按键id,由代码自动分配
	
	/* 按键硬件 */
	GPIO_TypeDef *GPIOx;				// 按键的端口
	uint16_t GPIO_Pin;					// 按键的引脚
	
	/* 按键的状态 */
	uint16_t keyDownCnt;				// 按键按下的计数值
	uint16_t keyUpCnt;					// 按键抬起的计数值
	uint16_t keyManyCliCnt;				// 按键多击计数
	KEY_STATE kDownLockState;			// 被按下的按键当前已解锁的状态
}key_Class;

/* 按键链表结构体 */
struct Key_Link
{
	key_Class kC;					// 按键类
	
	struct Key_Link * p_kN;			// 指向下一个节点的指针
};

/* 用户按键结构体 */
typedef struct
{
	/* 代号 */
	uint16_t keyID;				// 按键id,由代码自动分配
	
	/* 按键硬件 */
	GPIO_TypeDef *GPIOx;		// 按键的端口
	uint16_t GPIO_Pin;			// 按键的引脚
	
	/* 硬件引脚打印用值 */
	uint8_t portname;			// 端口的名称
	uint16_t pinName;			// 引脚的名称
}User_Key;



/**********************
 * GLOBAL PROTOTYPES
 **********************/

HAL_StatusTypeDef openKey_init(void);
HAL_StatusTypeDef key_add(User_Key * userKey, uint8_t * keyName, GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
HAL_StatusTypeDef key_clear(User_Key * userKey);
void key_scan(void);


uint16_t get_key_Num(void);
HAL_StatusTypeDef get_key_Name(User_Key * userKey, uint8_t * nameReadBuff);


/* 弱函数外部引用(可以重写,不用调用) */
void key_click(uint16_t key_id);
#if KEY_SUPPORT >= 2
void key_double(uint16_t key_id);
#endif
#if KEY_SUPPORT >= 3
void key_third(uint16_t key_id);
#endif
#if KEY_SUPPORT >= 4
void key_fourth(uint16_t key_id);
#endif
void key_long(uint16_t key_id);


#ifdef __cplusplus
} /*extern "C"*/
#endif

#endif /* __KEY_H_ */

使用

初次使用

在 yxy_open_key.h 中更改对应的宏配置

image-20221017102632789

KEY_SUPPORT

  • 按键多击支持(默认支持单击和长按)
  • >=2 则可支持2击
  • >=3 则可支持2、3击
  • >=4 则可支持2、3、4击

KEY_MAX_NUM

  • 可添加按键的最大数量。

KEY_NUME_LEN_MAX

  • 用户自定义的按键名字的最大长度。

KEY_Jitters_Elimination

  • 用户可定义的按键消抖时间,单位ms。

TIME_BASE_SLOT

  • 时间段落的长度,用于判断按键的不同击打模式的时间基准,单位ms。

main.c

main函数外需要添加的部分

头文件

#include "O_redirect.h"
#include "yxy_debug.h"
#include "yxy_open_key.h"

需要定义的全局变量

// 用户的按键对象,用于初始化按键,以及在按键回调中,匹配按键id,
// 或者用于获取按键的打印信息
User_Key key0;

重写按键回调函数

// 单击回调函数
void key_click(uint16_t key_id)
{
	if(key_id == key0.keyID)
	{
		U_Printf("key0 click\r\n");
	}
}

// 双击回调函数
void key_double(uint16_t key_id)
{
	if(key_id == key0.keyID)
	{
		U_Printf("key0 double\r\n");
	}
}

// 三击回调函数
void key_third(uint16_t key_id)
{
	if(key_id == key0.keyID)
	{
		U_Printf("key0 third\r\n");
	}
}

// 四击回调函数
void key_fourth(uint16_t key_id)
{
	if(key_id == key0.keyID)
	{
		U_Printf("key0 fourth\r\n");
	}
}

// 长按回调函数
void key_long(uint16_t key_id)
{
	if(key_id == key0.keyID)
	{
		U_Printf("key0 long\r\n");
	}
}

main函数非循环部分

// 开源按键初始化,这个是必须的
openKey_init();

// 根据自己的按键情况,添加按键进来
key_add(&Key0, (uint8_t *)"first key", KEY0_GPIO_Port, KEY0_Pin);

while循环中

// 对应的按键扫描函数,提供时间基准,时间基准最好在 1ms 左右
// 以及扫描按键链表上各个按键的状态,并调用对应的按键事件回调函数
key_scan();

HAL_Delay(1);


完整代码

开源按键完整工程代码点这里

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
下面是一个简链表屏幕滚动代码,适用于基于STM32F103芯片的TFT屏幕。代码使用了HAL库和FreeRTOS操作系统,并且使用了一个链表来存储屏幕上的文本。代码中的屏幕滚动是通过定时器中断实现的。 ```c #include "stm32f1xx_hal.h" #include "FreeRTOS.h" #include "task.h" #include "timers.h" #define LCD_WIDTH 240 #define LCD_HEIGHT 320 typedef struct _Node { char text[20]; struct _Node* prev; struct _Node* next; } Node; Node* head = NULL; Node* tail = NULL; void lcd_put_text(char* str, int x, int y) { // 在指定位置绘制文本 // 省略具体实现 } void lcd_clear(void) { // 清屏 // 省略具体实现 } void lcd_scroll_up(void) { // 滚屏 // 省略具体实现 } void lcd_add_text(char* str) { // 添加新文本到链表尾部 Node* node = (Node*)malloc(sizeof(Node)); strncpy(node->text, str, sizeof(node->text)); node->prev = tail; node->next = NULL; if (tail != NULL) { tail->next = node; } else { head = node; } tail = node; } void lcd_init(void) { // 初始化LCD // 省略具体实现 } void lcd_task(void* pvParameters) { while (1) { if (head != NULL) { lcd_scroll_up(); lcd_put_text(tail->text, 0, LCD_HEIGHT - 16); } vTaskDelay(500 / portTICK_PERIOD_MS); } } void timer_callback(TimerHandle_t xTimer) { // 添加新文本到链表 lcd_add_text("Hello, world!"); } int main(void) { // 初始化 HAL 库和 FreeRTOS HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); MX_USART1_UART_Init(); MX_DMA_Init(); // 初始化 LCD lcd_init(); // 创建定时器 TimerHandle_t timer = xTimerCreate("Timer", 1000 / portTICK_PERIOD_MS, pdTRUE, (void*)0, timer_callback); xTimerStart(timer, 0); // 创建任务 xTaskCreate(lcd_task, "LCD", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); // 启动 FreeRTOS 调度器 vTaskStartScheduler(); while (1) {} return 0; } ``` 上述代码中,lcd_add_text() 函数用于将新文本添加到链表尾部。lcd_task() 函数是一个FreeRTOS任务,它每隔一段时间检查链表头是否为空,如果不为空,则执行滚屏操作并在底部添加最新的文本。timer_callback() 函数是定时器回调函数,它每隔一段时间向链表中添加一个新的文本。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入一下?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值