在STM32上创建链表并实现LCD滚动显示串口消息

  在实现STM32开发ESP8266的时候发现ESP8266串口发送的消息行数很多, 如果使用普通的数组来存储消息需要大量的存储开销, 并且数据的显示也会损耗MCU的处理速度, 故而实现对消息的传输装入一个可以动态拓展, 并且具有灵活的调用形式的容器. 链表理所当然成为首选, 2019年更新: 将链表更新为循环链表.

  关于C语言链表的相关操作本文不再详细叙述, 若有需求请移步网址:https://blog.csdn.net/morixinguan/article/details/68951912先学习链表后再来学习在STM32创建链表.

  首先粘贴STM32上链表.c文件的代码:

#include "roll_display.h"
#include <string.h>
#include <stdlib.h>

Linkedlist *creatList(int size)       //创建一个普通的链表
{
	int i;
	Linkedlist *head = (Linkedlist*)malloc(sizeof(Linkedlist));
	Linkedlist *temp = head;
	for (i=0;i<size;i++)
	{
		Linkedlist *tail = (Linkedlist*)malloc(sizeof(Linkedlist));  //节点
		tail->next = NULL;    //确保下一次迭代时为空
		temp->next = tail;    //指向下一个节点
		temp = tail;          //迭代
	}
	return head;
}

Linkedlist *closeList(Linkedlist *list)				//闭合一个链表,返回末尾指针
{
	Linkedlist *temp = list;
	while (temp->next != NULL)
		temp = temp->next;
	temp->next = list;
	return temp;
}














 

接着是.h文件的代码:

#ifndef _ROLL_DISPLAY_H
#define _ROLL_DISPLAY_H
#include <sys.h>
#include <string.h>
#include <stdlib.h>

struct List         //节点
{
	uint8_t Data[20];
	struct List *next;
};

typedef struct List Linkedlist;

Linkedlist *creatList(int size);
Linkedlist *closeList(Linkedlist *list);




#endif

然后在主函数中调用代码时发现 编译会报错, 错误信息为//__use_no_semihosting was requested, but _ttywrch was 

大概意思为 编译需要使用半主机模式, 原因是因为malloc函数和free函数在MCU上有些不适用, 需要进行重定义 ttywrch函数

其实重定义函数在学习usart串口通信时已经使用过, 当时重定义的是printf函数使用的fputs函数

那么本次创建链表需要重定义ttywrch函数,具体代码为在代码任意处使用定义:
 

_ttywrch(int ch)        //为了避免malloc的定义错误  半主机模式
{  
}

 

这样就将ttywrch函数定义为一个无用函数, 编译就没有错误啦.

 

但是! 在之后使用链表时, 比如将创建的链表中Data数组的数据在串口中打印出来时发现打印的消息和想象中完全不符合!

之后经过我大量的检查后得出结论, 可能调用函数创建链表的栈堆不够使用造成的! 然后我打开STM32的启动文件, 也就是    

图中的startup_stm32f40_41xxx.c, 将其中的Heap_Size

改成了0x00002000 , 这样就是链表可使用的栈堆加大, 再次使用时就发现, 链表已经可以使用了!

至此STM32上链表的创建已经完成!

 

最后讲一下LCD上滚动显示串口消息的思路,:
创建一个具有10个节点的链表, 每次将新接收到的消息放在一个新的节点中, 并将其添加到链表的最后, 之后删除头节点, 成为一个新的链表, 对应LCD顺序显示每一个节点的消息, 并实现迭代.

代码如下:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "string.h"
#include "key.h"
#include "roll_display.h"

#define MES_SIZE	10    //定义的循环链表长度

//__use_no_semihosting was requested, but _ttywrch was 
_ttywrch(int ch)        //为了避免malloc的定义错误  半主机错误
{  
}

uint8_t n,line=50;     //line 第几行;s

int main(void)
{
	uint8_t i,len;
	Linkedlist *head = creatList(MES_SIZE-1); 		//创建一个有10个节点的链表
	Linkedlist *temp = closeList(head);				//闭合该链表,并返回末尾指针
							
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);      //初始化延时函数
	uart_init(9600);		//初始化串口波特率为115200
	LED_Init();					  //初始化LED
 	LCD_Init();           //初始化LCD FSMC接口
	POINT_COLOR=RED;      //画笔颜色:红色
	
	LCD_ShowString(50,10,210,24,16,"Rolling Data:");
	
	while(1)
	{				
		if (USART_RX_STA&0x8000)
		{	
			len = USART_RX_STA&0x3FFF;
			LCD_Fill(20,line,240,246,WHITE);       //对动态显示行清除显示
									
			memcpy(temp->Data,USART_RX_BUF,len);	//将数据写入循环链表的末尾
			temp = temp->next;			//指向下一个指针,一个循环后变为头指针
			
			for (i=0;i<MES_SIZE;i++)			//遍历全部的链表节点
			{
				LCD_ShowString(20,line,240,12,16,temp->Data);    //每次对下一行显示
				temp = temp->next;				//回到起点指针
				line += 20;   	
			} 
			line=50;                             //底行			
			USART_RX_STA=0;
			memset(USART_RX_BUF,0,len);          //清除接收缓存
		}			
	}
}


 嗯, 操作已经分享结束, 下面靠你们自己了啊! 加油! 不懂的可以在下面留言, 我看见的话会来解答.

 

若觉文章有助于你,可以在下面留言点赞哦 (<_>)!

若转载本文请注明出处

  • 7
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
实现此功能需要以下步骤: 1. 创建一个链表结构体,用来存储串口消息。 2. 在串口中断函数中,将接收到的消息插入到链表的尾部。 3. 在LCD显示函数中,从链表的头部开始遍历,将消息逐个显示LCD上。 4. 如果链表中的消息数量超过了LCD显示区域的限制,就需要进行滚动显示,即将链表头部的消息删除,并将链表指针指向下一个节点。 下面是一个简单的示例代码: ```c #include "stm32f4xx.h" #include "lcd.h" #define MAX_MSG_NUM 10 // 链表最大存储消息数量 typedef struct MsgNode_t { char msg[32]; struct MsgNode_t* next; } MsgNode; MsgNode* head = NULL; // 链表头指针 MsgNode* tail = NULL; // 链表尾指针 uint8_t msg_num = 0; // 链表当前存储消息数量 void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { char c = USART_ReceiveData(USART1); USART_ClearITPendingBit(USART1, USART_IT_RXNE); if (msg_num >= MAX_MSG_NUM) { // 链表已满,删除头部消息 MsgNode* tmp = head; head = head->next; free(tmp); msg_num--; } // 插入尾部节点 MsgNode* node = (MsgNode*)malloc(sizeof(MsgNode)); strcpy(node->msg, c); node->next = NULL; if (head == NULL) { head = node; tail = node; } else { tail->next = node; tail = node; } msg_num++; } } void lcd_scroll_display(void) { int i; MsgNode* node = head; for (i = 0; i < MAX_MSG_NUM; i++) { if (node == NULL) { break; } lcd_display_string(node->msg); node = node->next; } if (i >= MAX_MSG_NUM) { // 链表已满,需要滚动显示 MsgNode* tmp = head; head = head->next; free(tmp); msg_num--; } } int main(void) { // 初始化串口LCD USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx; USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_Cmd(USART1, ENABLE); lcd_init(); while (1) { lcd_scroll_display(); } } ``` 需要注意的是,链表的操作需要注意多线程安全性,因为串口中断函数和LCD显示函数可能同时访问链表。建议使用互斥锁或者其他同步机制来保证数据的正确性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值