【开源】重磅!基于迪文屏的DE-RTOS第一篇Shell
本帖最后由 二哲科技 于 2023-9-16 15:49 编辑1、介绍
迪文屏的开发者非常的多,但是迪文没有一个很好的生态系统,像国产RTThread一样,拥有多种组件,多种传感器,能够快速的实现产品开发,为此我设计了一款基于C51的DE-RTOS,希望能成为迪文的操作系统之一。
该系统是参考RTThread的文件结构,也为了后续方便移植RTThread的各种组件和软件包,RTThread的系统框架如下图所示:
2、设计难点
目前基于C51的操作系统不多,RTX51系列是不开源的,所以C51的任务跳转也是比较难实现的,我也在找各种方法,后续还准备设计一款类似STM32CubeMX的图形配置软件,可以直接通过图形化进行外设、组件和软件包的配置,这个软件周期也会比较长,需要先完善DE-RTOS之后再去设计图形化配置软件。下面是STM32CubeMX的软件界面。
3、设计
1)代码设计
需要设计好文件框架,我是直接参考RTThread的文件框架,同时添加上一些迪文和自己的内容,文件框架如下图所示:
接下来就是移植Shell,在移植Shell的时候,其实就移植了一部分的RTThread,这部分移植还是挺复杂的,需要对串口数据进行FIFO的处理,然后再进行解码,根据解码的结果去调用合适的函数。调用函数用到了函数指针,这样就可以根据字符串的内容去执行相关函数,同时还可以传递数据。
大概贴一下Shell函数解析的代码。
void shell_task_entry()
{
static uint8_t frist_flag = 0;
char ch = 0;
int8_t ret = 0;
if(frist_flag == 0)
{
frist_flag = 1;
shell.echo_mode = 1;
printf(FINSH_PROMPT);
}
//while(1)
{
ch = de_hw_console_getchar();
if(ch == 0)
{
return;
}
// printf("%c", ch);
/*
* handle control key
* up key: 0x1b 0x5b 0x41
* down key: 0x1b 0x5b 0x42
* right key:0x1b 0x5b 0x43
* left key: 0x1b 0x5b 0x44
*/
if (ch == 0x1b)
{
shell.stat = WAIT_SPEC_KEY;
return;
}
else if (shell.stat == WAIT_SPEC_KEY)
{
if (ch == 0x5b)
{
shell.stat = WAIT_FUNC_KEY;
return;
}
shell.stat = WAIT_NORMAL;
}
else if (shell.stat == WAIT_FUNC_KEY)
{
shell.stat = WAIT_NORMAL;
if (ch == 0x41) /* up key */
{
#ifdef FINSH_USING_HISTORY
/* prev history */
if (shell.current_history > 0)
shell.current_history --;
else
{
shell.current_history = 0;
return;
}
/* copy the history command */
memcpy(shell.line, &shell.cmd_history,
FINSH_CMD_SIZE);
shell.line_curpos = shell.line_position = strlen(shell.line);
shell_handle_history(&shell);
#endif
return;
}
else if (ch == 0x42) /* down key */
{
#ifdef FINSH_USING_HISTORY
/* next history */
if (shell.current_history < shell.history_count - 1)
shell.current_history ++;
else
{
/* set to the end of history */
if (shell.history_count != 0)
shell.current_history = shell.history_count - 1;
else
return;
}
memcpy(shell.line, &shell.cmd_history,
FINSH_CMD_SIZE);
shell.line_curpos = shell.line_position = strlen(shell.line);
shell_handle_history(&shell);
#endif
return;
}
else if (ch == 0x44) /* left key */
{
if (shell.line_curpos)
{
printf("\b");
shell.line_curpos --;
}
return;
}
else if (ch == 0x43) /* right key */
{
if (shell.line_curpos < shell.line_position)
{
printf("%c", shell.line);
shell.line_curpos ++;
}
return;
}
}
/* received null or error */
if (ch == '\0' || ch == 0xFF) return;
/* handle tab key */
#ifdef FINSH_USING_SYMTAB
else if (ch == '\t')
{
int i;
/* move the cursor to the beginning of line */
for (i = 0; i < shell.line_curpos; i++)
printf("\b");
/* auto complete */
shell_auto_complete(&shell.line);
/* re-calculate position */
shell.line_curpos = shell.line_position = strlen(shell.line);
return;
}
#endif
/* handle backspace key */
else if (ch == 0x7f || ch == 0x08)
{
/* note that shell.line_curpos >= 0 */
if (shell.line_curpos == 0)
return;
shell.line_position--;
shell.line_curpos--;
if (shell.line_position > shell.line_curpos)
{
int i;
de_memmove(&shell.line,
&shell.line,
shell.line_position - shell.line_curpos);
shell.line = 0;
printf("\b%s\b", &shell.line);
/* move the cursor to the origin position */
for (i = shell.line_curpos; i <= shell.line_position; i++)
printf("\b");
}
else
{
printf("\b \b");
shell.line = 0;
}
return;
}
/* handle end of line, break */
if (ch == '\r' || ch == '\n')
{
#ifdef FINSH_USING_HISTORY
shell_push_history(&shell);
#endif
#ifdef FINSH_USING_MSH
shell_screen_append(0, shell.line, shell.line_position);
if (cmd_is_used() == DE_TRUE)
{
if (shell.echo_mode)
printf("\r\n");
cmd_exec(shell.line, shell.line_position);
}
else
#else
printf("\r\n");
#endif
{
#ifndef FINSH_USING_MSH_ONLY
/* add ';' and run the command line */
shell.line = ';';
if (shell.line_position != 0)
finsh_run_line(&shell.parser, shell.line);
else
if (shell.echo_mode) printf("\n");
#endif
}
printf(FINSH_PROMPT);
memset(shell.line, 0, sizeof(shell.line));
shell.line_curpos = shell.line_position = 0;
return;
}
/* it's a large line, discard it */
if (shell.line_position >= FINSH_CMD_SIZE)
shell.line_position = 0;
/* normal character */
if (shell.line_curpos < shell.line_position)
{
int i;
de_memmove(&shell.line,
&shell.line,
shell.line_position - shell.line_curpos);
shell.line = ch;
if (shell.echo_mode)
printf("%s", &shell.line);
/* move the cursor to new position */
for (i = shell.line_curpos; i < shell.line_position; i++)
printf("\b");
}
else
{
shell.line = ch;
if (shell.echo_mode)
printf("%c", ch);
}
ch = 0;
shell.line_position ++;
shell.line_curpos++;
if (shell.line_position >= FINSH_CMD_SIZE)
{
/* clear command line */
shell.line_position = 0;
shell.line_curpos = 0;
}
} /* end of device read */
}
然后就是判断执行什么函数,如果输入内容错误,则直接返回错误提示。
void cmd_exec(char *cmd_line, uint8_t cmd_len)
{
if(cmd_len > 0)
{
uint8_t i = 0;
char cmd_line_temp = {0};
memcpy(cmd_line_temp, cmd_line, cmd_len);
for(i = 0;i < cmd_cnt;i++)
{
if(memcmp(cmd_list.name, cmd_line_temp, strlen(cmd_list.name)) == 0 && cmd_len >= strlen(cmd_list.name))
{
cmd_list.function(cmd_line_temp);
//printf("ret:%d\r\n", cmd_list.function(cmd_line_temp));
break;
}
}
if(i == cmd_cnt)
{
cmd_error(cmd_line_temp);
}
}
}
同时我还设计了控制屏幕控件的函数,可以不用阅读【T5L_DGUSII_应用开发指南】就可以对控件进行操作,目前只设计了文本和基本图形中的矩形,后续会完善剩下的,代码如下所示:
void base_graph_rectangle_fill_set(uint16_t addr, base_graph_param_t base_graph_param_temp[], uint8_t len)
{
uint8_t cmd = {0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, 0x03, 0x20, 0xAA, 0xAA, 0xFF, 0x00};
uint8_t i = 0;
if(len > 24) //一次最多24个数据
len = 24;
//图形数量
cmd = len;
for(i = 0;i < len;i++)
{
cmd = base_graph_param_temp.start_x / 256;
cmd = base_graph_param_temp.start_x % 256;
cmd = base_graph_param_temp.start_y / 256;
cmd = base_graph_param_temp.start_y % 256;
cmd = base_graph_param_temp.end_x / 256;
cmd = base_graph_param_temp.end_x % 256;
cmd = base_graph_param_temp.end_y / 256;
cmd = base_graph_param_temp.end_y % 256;
cmd = base_graph_param_temp.color / 256;
cmd = base_graph_param_temp.color % 256;
}
cmd = 0xFF;
cmd = 0x00;
sys_write_vp(addr, cmd, (len * 10 + 6) / 2);
}
//文本变量设置
void text_display_value_set(uint16_t addr, uint16_t offset, char *text_data, uint16_t text_len)
{
sys_write_vp(addr + offset, text_data, text_len);
}
//文本描述设置
void text_display_describe_set(uint16_t addr, uint32_t mask, uint16_t value_addr, int16_t x, int16_t y)
{
if(mask & TEXT_MASK_VALUE_ADDR == TEXT_MASK_VALUE_ADDR)
{
sys_write_vp(addr, (uint8_t *)&value_addr, 1);
}
}
最后还有一个触摸的回调,通过注册按键、添加按键回调来实现按键的触发,保证了系统的分层调用关系。代码如下:
void touch_init(uint16_t touch_addr)
{
touch_address = touch_addr;
}
void touch_insert(uint16_t touch_value, touch_function_t touch_function_temp)
{
touch_function = touch_function_temp;
touch_values = touch_value;
touch_function_cnt++;
}
//用于产生按钮点击扫描信号,放在1ms的定时器中调用即可
void touch_tick()
{
#define BTN_SCAN_PERIOD 100 //按钮点击事件的扫描周期,单位ms
static idata uint8_t tick = 0;
tick++;
if(tick == BTN_SCAN_PERIOD)
{
tick = 0;
is_btn_scan = 1;//产生一个按钮点击扫描信号
}
}
//按钮点击事件扫描并处理
void touch_handler()
{
uint16_t btn_val;
uint8_t i = 0;
if(is_btn_scan == 0)//是否有扫描信号,没有的话就啥也不做,直接返回
return;
is_btn_scan = 0;//清除
//1.开始检查是否有按钮按下
sys_read_vp(touch_address, (uint8_t*)&btn_val, 1);
if(btn_val == 0)
return;
//2.按钮点击事件处理
for(i = 0;i < touch_function_cnt;i++)
{
if(btn_val == touch_values)
touch_function();
}
//3.清除按键值
btn_val = 0;
sys_write_vp(touch_address, (uint8_t*)&btn_val, 1);
}
2)界面设计
界面设计就是简单的显示Shell的输入和输出,不过也用到了一些新技术,通过修改变量地址来实现翻页,这样就非常方便,界面素材只有三张图。
一共就两个界面,首页和显示页,有技术难度的在显示页,可以实现自动翻页和自动换行功能,主要是通过代码实现的,具体如下:
#define PAGE_LINE_MAX 17//一页最多十七行
uint8_t page_total = 0; //总页数
uint8_t page_current = 0; //当前页数
uint8_t last_page_lines = 0; //最后一页的行数
uint8_t last_page_length = 0; //最后一页的字符数
uint16_t page_addr = 0x2000;
void display_page_status()
{
char page_temp = {0};
page_temp = (page_current + 1) / 10 + '0';
page_temp = (page_current + 1) % 10 + '0';
page_temp = '/';
page_temp = (page_total + 1) / 10 + '0';
page_temp = (page_total + 1) % 10 + '0';
text_display_value_set(0x1100, 0, page_temp, 3);
}
void shell_screen_append(uint8_t mode, char *line_data, uint8_t line_len)
{
char line_temp = {0};
char line_cnt = 0;
uint8_t i = 0;
uint8_t current_line_cnt = 0;
if(mode == 0)
{
memcpy(line_temp, "msh >", 5);
line_cnt = 5;
}
if(page_current != page_total)
{
page_current = page_total;
text_display_describe_set(0x1200, TEXT_MASK_VALUE_ADDR, page_addr + page_current * 0x200, 0, 0);
}
for(i = 0;i < line_len;i++)
{
line_temp = line_data;
}
current_line_cnt = (line_cnt / 25) + 1;
last_page_lines += (line_cnt / 25) + 1;
if(last_page_lines > PAGE_LINE_MAX)
{
page_total++;
page_current = page_total;
last_page_lines = current_line_cnt;
last_page_length = 0;
text_display_describe_set(0x1200, TEXT_MASK_VALUE_ADDR, page_addr + page_current * 0x200, 0, 0);
display_page_status();
}
if(line_cnt % 2 == 1)
{
if(line_cnt % 25 < 23)
{
line_temp = ' ';
line_temp = '\r';
line_temp = '\n';
}
else if(line_cnt % 25 == 24)
{
line_temp = ' ';
}
}
else
{
if(line_cnt % 25 < 24)
{
line_temp = '\r';
line_temp = '\n';
}
}
// printf("\r\n%s %d\r\n", line_temp, line_len);
text_display_value_set(page_addr + page_current * 0x200, last_page_length / 2, line_temp, line_cnt / 2);
last_page_length += line_cnt;
}
void page_next()
{
// printf("page_next");
if(page_current < page_total)
{
page_current++;
text_display_describe_set(0x1200, TEXT_MASK_VALUE_ADDR, page_addr + page_current * 0x200, 0, 0);
display_page_status();
}
}
void page_last()
{
// printf("page_last");
if(page_current > 0)
{
page_current--;
text_display_describe_set(0x1200, TEXT_MASK_VALUE_ADDR, page_addr + page_current * 0x200, 0, 0);
display_page_status();
}
}
4、结论
C51移植Shell还是相对麻烦的,因为有些变量是没有的,所以需要通过别的方法来实现。好在整体功能都实现了,后续DE-RTOS希望有更多的开发者一起参与,来完善迪文的操作系统生态,我也会好好维护和升级DE-RTOS。
开源地址:https://gitee.com/erzhekeji/dwin_dertos.git
界面工程:
演示视频:https://www.bilibili.com/video/BV14h4y1A7wd/
好东西必须顶一个。 这个必须顶!
页:
[1]