当前位置:网站首页>Shell implementation based on stm32
Shell implementation based on stm32
2022-07-30 17:11:00 【@look big smart】
首先
思考一下,想要和linuxUse the same command,Execute the corresponding operation through the command,The first step is to define the command,These commands are artificially added and removed,Save to a storage location where we can find the command,The underlying rationale for implementing commands in code is to link each command to each other in the form of a linked list.
下面我将ubootThe code for linked lists in copy出来, 也可以自行去uboot中查找
#ifndef LIST_H
#define LIST_H
struct list_head {
struct list_head *next, *prev;
};
/****************************************************************/
#define LIST_HEAD_INIT(name) {
&(name), &(name) }
#define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name)
//This macro defines the method,while defining the node,并且初始化,if defined,Not suitable for this method
/****************************************************************/
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
//This method is after the node is defined externally,调用该函数,给成员赋值
/****************************************************************/
/* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */
#ifndef CONFIG_DEBUG_LIST
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
#else
extern void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next);
#endif
/** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */
#ifndef CONFIG_DEBUG_LIST
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
#else
extern void list_del(struct list_head *entry);
#endif
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
/** * container_of - cast a member of a structure out to the containing structure 根据结构体重的一个成员变量地址导出包含这个成员变量mem的struct地址 * @ptr: the pointer to the member. 结构体变量中某个成员的地址 * @type: the type of the container struct this is embedded in. 结构体类型 * @member: the name of the member within the struct. 该结构体变量的具体名字 * */
#define container_of(ptr, type, member) ({
\ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
#define list_entry(ptr, type, member) \ container_of(ptr, type, member)
#define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) \
#endif // LIST_H
到此,The data structure and methods of the linked list have been implemented,But this is just a simple linked list structure,并没有什么太大用处,Make it contain more parameters,重新封装一下,如下代码
//定义一个函数指针类型
typedef void (*pfunc_t)(void);
//栈内存
struct node
{
char *name;
char *usage;
int maxargs;
pfunc_t run_command;
struct list_head list;
};
/* 此代码的作用是初始化一个自定义名称的链表,把成员变量赋初值,并且将纯链表指向自己*/
/*链表节点*/
#define Struct_Section __attribute__ ((used,section (".shell")))
#define create_list(name,usage,maxargs,cmd) \ struct node name Struct_Section = {
#name,usage,maxargs,cmd, &(name.list), &(name.list) }
The above is the definition of the data structure of the command.
The following is to define your own commands using data structures.
LIST_HEAD(Head);
void CMD_LIST_Init(void)
{
static create_list(led,"select led mode\r\n"
" led 0 Led_Dark\r\n"
" led 1 Led_Light\r\n"
" led 2 Led_Light100ms\r\n"
" led 3 Led_Blink1\r\n"
" led 4 Led_Blink2\r\n"
" led 5 Led_Blink3\r\n"
" led 6 Led_Blink4\r\n"
" led 7 Led_FLOW\r\n",1,do_leds);
list_add_tail(&(led.list),&Head);
static create_list(help," print command usage",1,do_help);
list_add_tail(&(help.list),&Head);
static create_list(echo," echo args to console",1,do_echo);
list_add_tail(&(echo.list),&Head);
static create_list(board," echo slot board type",1,do_board);
list_add_tail(&(board.list),&Head);
static create_list(reboot," Reboot System",1,do_reboot);
list_add_tail(&(reboot.list),&Head);
static create_list(DP," echo DP Input status",1,do_DP);
list_add_tail(&(DP.list),&Head);
static create_list(readedid," Read EDID info",1,do_ReadEDID);
list_add_tail(&(readedid.list),&Head);
static create_list(earseedid," earse EDID",1,do_WriteEDID);
list_add_tail(&(earseedid.list),&Head);
static create_list(reset," reset xxx",1,do_reset);
list_add_tail(&(reset.list),&Head);
static create_list(HPD," HPD on or off",1,do_HPD);
list_add_tail(&(HPD.list),&Head);
}
在main函数中初始化CMD_LIST_Init后,运行一下看一下效果
使用help将定义好的usage打印出来,Above is the print.
Only using the above code can not complete the interaction of normal users,The above print information is also debugged by me,When the user's input cannot be obtained, the interaction cannot be performed.
使用SecureCRT打开串口,It will be found when the serial port is opened to receive,We enter a key on the keyboard,will enter the serial port interrupt,也就是说SecureCRTIt is to send and receive data through the current interface.
A test can be done here,Use the serial port to send the echoed code to operate,The normal use of other tools may be as shown below
原理很简单,串口接收到数据,并打印数据
// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
{
ucTemp = USART_ReceiveData(DEBUG_USARTx);
USART_SendData(DEBUG_USARTx,ucTemp);
}
}
测试使用SecureCRT测试,The keys pressed on the keyboard can be printed,也就是说SecureCRTThe key data input into this window can be directly sent to the receiving device through the serial port,stm32Or after other devices receive the data,将接收到的数据打印出来,也就是说,The data we see in the figure below is not directly entered into the window by the keyboard,It is the data sent by the microcontroller.SecureCRTof this property is the followingshellCode writing defines a scene.
Redefine serial port interrupts,如下所示,关于串口的DMA可以不管,shell没有使用
//串口1接收中断
void USART1_IRQHandler(void)
{
u8 num=0;
if(USART_GetITStatus(USART1,USART_IT_IDLE) == SET)//空闲中断
{
num = USART1->SR;
num = USART1->DR;
DMA_Cmd(DMA1_Channel5,DISABLE);
num = U1_BUFFER_LEN - DMA_GetCurrDataCounter(DMA1_Channel5);
u1_RxBuffer[num] = 0;
DMA1_Channel5->CNDTR=U1_BUFFER_LEN;
DMA_Cmd(DMA1_Channel5,ENABLE);
u1_flag = num;
}
else if(USART_GetITStatus(USART1,USART_IT_RXNE) == RESET)//接收中断
{
char ch;
ch = USART_ReceiveData(USART1);
Termial_get_str(&ch);
}
}
My idea here is to make a lightweight oneshellTools for debugging and testing,而不是复杂的shell,基于此目标,Don't do much compatibility design.
when getting a string,直接进入Termial_get_str()进行数据解析.Post the parsing code,This is also the essence of this article,But there is no technical content
char buf[256] = {
0}; // Used to temporarily store commands entered by the user
char *p_Move = buf;
void Termial_get_str(char* ch)
{
switch(*ch)
{
case 0x03:{
/*CTRL C*/
DP_input =0;
}
case 0x0D:{
/*换行*/
parse_line(buf); /*命令解析*/
find_run_cmd(); /*查找命令*/
Usart_SendString(USART1,"\r\n");
Usart_SendString(USART1,"shell:~$ ");
memset(buf, 0, sizeof(buf));
p_Move = buf;
break;
}
case 0x08:{
if (p_Move > buf)
{
Usart_SendByte(USART1,0x08);
Usart_SendString(USART1," ");
Usart_SendByte(USART1,0x08);
p_Move--; // 退一格,The pointer points to the cell to be deleted
*p_Move = '\0'; // 填充'\0'to replace the character you want to delete
}
break;
}
default:{
USART_SendData(USART1,*ch); // 回显
*p_Move++ = *ch; // 存储ch,等效于 *p = ch; p++;
break;
}
}
}
键值:0x03 CTRL + C
键值:0x0D 换行
键值:0x08 Backspace
键值:其余 正常输出
0x03on the buttonCTRL + C的键值,When received data equals0x03时,Clear the flag bit of the data,Printing output by means of macro definition has a certain effect,例如
#define DEBUG(fmt,arg...) do{
\ if(DP_input)\ printf("[ DEBUG ] [%d]"fmt"\r\n",__LINE__, ##arg);\ }while(0)
Backspace原理与上面相同,如果觉得不够直观,可以通过使用keil的DebugView the digital data directly for verification.
when a newline is received,Perform a command parsing,并将shellThe command line is refreshed again.
case 0x0D:{
/*换行*/
parse_line(buf); /*命令解析*/
find_run_cmd(); /*查找命令*/
Usart_SendString(USART1,"\r\n");
Usart_SendString(USART1,"shell:~$ ");
memset(buf, 0, sizeof(buf));
p_Move = buf;
break;
}
After getting the data entered by the user,由Termial_get_str可知,数据是保存在buf中,接收到换行,Defined as the user enters a command.After getting the command,A long string needs to be decomposed,原理是通过空格进行分解,The following is the disassembly function,The debug echo function is not required
void parse_line(char* buf)
{
int count = 0;
char *p = strtok(buf," ");
while(p) //遍历输出
{
argv[count] = p;
p = strtok(NULL," "); //指向下一个
count++;
if(count >= ARGC_MAX)//最多接收10个
{
break;
}
}
argc = count;
#if 0
/**********Debug echo range**********/
if(count != 0)
{
msg("\r\nargc = %d",argc);
}
for (int i = 0; i<count; i++)
{
msg("\r\nargv[%d] = %s",i,argv[i]);
}
/**********Debug echo range**********/
#endif
}
到这里,The data entered by the user has been saved separately toargv中,Finally, the corresponding action can be performed by parsing the string
int find_run_cmd(void)
{
struct node *pos;
int find_cmd = 0;
list_for_each_entry(pos,&Head,list)
{
if(strcmp(pos->name,argv[0]) == 0) /* '\0'的ascii码是0 */
{
pos->run_command();//执行命令
find_cmd = 1;
}
}
if( *(argv[0]) != 0 && *(argv[0]) != 0x20 && find_cmd == 0)
{
msg(\r\nunknown command: "%s" \r\n,argv[0]);
}
return 1;
}
pos->run_command();//执行命令
This corresponds to the beginning of the articledo_help、do_ledsand so on these execution functions
Demonstrate a print function,比较直观,逻辑比较简单
static create_list(board," echo slot board type",1,do_board);
list_add_tail(&(board.list),&Head);
Defined at initialization`board`命令,下面是 do_board函数
msg(\r\n\r\n);
msg(" _____ _____ ____ ____ _____ _____ "\r\n);
msg("| __ \| __ \ | _ \ / __ \ /\ | __ \| __ \ "\r\n);
msg("| | | | |__) | | |_) | | | | / \ | |__) | | | |"\r\n);
msg("| | | | ___/ | _ <| | | |/ /\ \ | _ /| | | |"\r\n);
msg("| |__| | | | |_) | |__| / ____ \| | \ \| |__| |"\r\n);
msg("|_____/|_| |____/ \____/_/ \_\_| \_\_____/ "\r\n);
msg("____________________________________________________"\r\n);
msg("[ DP App Version: 1.2.0 ] -- [ DATE: %s ] -- [ TIME: %s ]"\r\n,__DATE__,__TIME__);

Additional explanations will follow,If you have any questions, you can leave a message below.
Finally, share a simple one at the same timeshell代码,解压即用,芯片用的stm32f103c8t6
https://download.csdn.net/download/weixin_44748127/45073339
边栏推荐
- How does the new retail saas applet explore the way to break the digital store?
- [MRCTF2020]Ezaudit
- 数据的存储
- DTSE Tech Talk丨Phase 2: 1 hour in-depth interpretation of SaaS application system design
- C# 连接SQL Sever 数据库与数据查询实例 数据仓库
- huato 热更新环境搭建(DLL方式热更新C#代码)
- 阿里巴巴CAN:Embedding前置的特征交互新思路
- Oracle动态监听与静态监听详解
- PHP留言反馈管理系统源码
- esp32系列(5):esp32 蓝牙架构学习
猜你喜欢

你是一流的输家,你因此成为一流的赢家

浅谈在线编辑器中增量编译技术的应用

HUAWEI CLOUD data governance production line DataArts, let "data 'wisdom' speak"

MySQL详细学习教程(建议收藏)

OpenCV形状检测

Gorilla Mux 和 GORM 的使用方法

BCryptPasswordEncoder的使用及原理

Login Module Debugging - Getting Started with Software Debugging

登录模块调试-软件调试入门

Error occurred while trying to proxy request项目突然起不来了
随机推荐
向量检索基础方法总结
[HarekazeCTF2019] Avatar Uploader 1
Gorilla Mux 和 GORM 的使用方法
huato hot update environment construction (DLL method hot update C# code)
说几个大厂分库分表的那点破事。
The first time I used debug query and found that this was empty, does it mean that the database has not been obtained yet?please help.
对话框 QDialog ( 详解 )
DTSE Tech Talk丨Phase 2: 1 hour in-depth interpretation of SaaS application system design
[Geek Challenge 2020] Roamphp1-Welcome
京东获取推荐商品列表 API
C# 连接SQL Sever 数据库与数据查询实例 数据仓库
The way of life, share with you!
onenote使用
一篇文 带你搞懂,虚拟内存、内存分页、分段、段页式内存管理(超详细)
WeChat applet picker scroll selector use detailed explanation
Weka 3.8.6安装与Weka 3.8.6功能介绍
C# 跨程序传图(共享内存块传图)跨exe传图
第一次用debug查询,发现这个为空,是不是代表还没获得数据库的意思?求帮助。
Excel导入和导出
Login Module Debugging - Getting Started with Software Debugging