|
本帖最后由 二哲科技 于 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[shell.current_history][0],
- 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[shell.current_history][0],
- 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]);
- 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[0]);
- /* 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_curpos],
- &shell.line[shell.line_curpos + 1],
- shell.line_position - shell.line_curpos);
- shell.line[shell.line_position] = 0;
- printf("\b%s \b", &shell.line[shell.line_curpos]);
- /* 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[shell.line_position] = 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[shell.line_position] = ';';
- 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_curpos + 1],
- &shell.line[shell.line_curpos],
- shell.line_position - shell.line_curpos);
- shell.line[shell.line_curpos] = ch;
- if (shell.echo_mode)
- printf("%s", &shell.line[shell.line_curpos]);
- /* move the cursor to new position */
- for (i = shell.line_curpos; i < shell.line_position; i++)
- printf("\b");
- }
- else
- {
- shell.line[shell.line_position] = 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[30] = {0};
- memcpy(cmd_line_temp, cmd_line, cmd_len);
- for(i = 0;i < cmd_cnt;i++)
- {
- if(memcmp(cmd_list[i].name, cmd_line_temp, strlen(cmd_list[i].name)) == 0 && cmd_len >= strlen(cmd_list[i].name))
- {
- cmd_list[i].function(cmd_line_temp);
- //printf("ret:%d\r\n", cmd_list[i].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[252] = {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[3] = len;
- for(i = 0;i < len;i++)
- {
- cmd[4 + i * 10] = base_graph_param_temp[i].start_x / 256;
- cmd[5 + i * 10] = base_graph_param_temp[i].start_x % 256;
- cmd[6 + i * 10] = base_graph_param_temp[i].start_y / 256;
- cmd[7 + i * 10] = base_graph_param_temp[i].start_y % 256;
- cmd[8 + i * 10] = base_graph_param_temp[i].end_x / 256;
- cmd[9 + i * 10] = base_graph_param_temp[i].end_x % 256;
- cmd[10 + i * 10] = base_graph_param_temp[i].end_y / 256;
- cmd[11 + i * 10] = base_graph_param_temp[i].end_y % 256;
- cmd[12 + i * 10] = base_graph_param_temp[i].color / 256;
- cmd[13 + i * 10] = base_graph_param_temp[i].color % 256;
- }
- cmd[len * 10 + 4] = 0xFF;
- cmd[len * 10 + 5] = 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_cnt] = touch_function_temp;
- touch_values[touch_function_cnt] = 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[i])
- touch_function[i]();
- }
- //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[6] = {0};
- page_temp[0] = (page_current + 1) / 10 + '0';
- page_temp[1] = (page_current + 1) % 10 + '0';
- page_temp[2] = '/';
- page_temp[3] = (page_total + 1) / 10 + '0';
- page_temp[4] = (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[100] = {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_cnt++] = line_data[i];
- }
- 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_cnt++] = ' ';
- line_temp[line_cnt++] = '\r';
- line_temp[line_cnt++] = '\n';
- }
- else if(line_cnt % 25 == 24)
- {
- line_temp[line_cnt++] = ' ';
- }
- }
- else
- {
- if(line_cnt % 25 < 24)
- {
- line_temp[line_cnt++] = '\r';
- line_temp[line_cnt++] = '\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。
界面工程:
演示视频:
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|