一个超级精简高可移植的shell命令行C实现

原创 嵌入式Lee 2023-11-21 08:01

一. 前言

  在嵌入式开发中,一般需要使用shell命令行来进行交互。在一些资源非常受限的平台比如8位单片机平台,则各种开源的shell框架都显得过于大和复杂。此时实现一个超级精简的shell命令行则显得很必要。我们这里就来实现一个,方便以后快速集成到任何平台中使用。

有几点开发前提:

1.考虑可移植性,不使用任何编译器扩展。

2.考虑可移植性,不依赖任何的库函数,仅依赖stdint的数据类型。

3.考虑可移植性, 核心代码无需任何修改,只需要调用初始化配置接口来适配不同平台。

4.尽可能少的代码量和ram使用量。

5.精简,一切都刚好够用,可以快速嵌入任何平台,针对不同需要可以直接在此基础上增加实现。

在上一篇中https://mp.weixin.qq.com/s/QSYKxND3DTyQZ0JtnSqkzQ实际已经使用了,也提到了要分享该实现。

二. 实现

首先定义一个数据结构,用于记录命令字符串和对应的执行函数和帮助字符串的对应关系

typedef void (*shell_command_pf)(uint8_t *);                      /**< 命令回调函数   */typedef uint32_t (*shell_read_pf)(uint8_t *buff, uint32_t len);   /**< 底层收接口     */typedef void (*shell_write_pf)(uint8_t *buff, uint32_t len);      /**< 底层接口     */
/** * \struct shell_cmd_cfg * 命令信息*/typedef struct{ uint8_t * name; /**< 命令字符串 */ shell_command_pf func; /**< 命令回调函数 */ uint8_t * helpstr; /**< 命令帮助信息 */ }shell_cmd_cfg;

对接口抽象,初始化指定输入输出接口

/** * \fn shell_set_itf * 设置底层输入输出接口,以及命令列表 * 调用shell_exec_shellcmd之前,需要先调用该接口初始化 * \param[in] input \ref shell_read_pf 输入接口 * \param[in] output \ref shell_write_pf 输出接口 * \param[in] cmd_list \ref shell_cmd_cfg 命令列表 * \param[in] enableecho 0:不使能回显, 其他值:使能回显*/void shell_set_itf(shell_read_pf input, shell_write_pf output, shell_cmd_cfg* cmd_list, uint8_t enableecho);

非阻塞,主循环调用以下函数进行处理

/** * \fn shell_exec * 周期调用该函数,读取底层输入,并判断是否有命令进行处理 * 非阻塞*/void shell_exec(void);

通过以下宏配置缓存大小,这里实际可以修改下改为调用者分配,而不是静态数组,这样可以由调用者决定分配和释放,更灵活,也在不用的时候不占用空间。

#define SHELL_CMD_LEN 64                                          /**< 命令缓冲区大小 */

核心逻辑

shell_exec调用 shell_read_line从输入接口读数据到缓冲区,读到一行后调用

shell_exec_cmdlist查询命令信息表,匹配字符串再调用对应的实现函数。

所以核心代码是读命令行,直接看注释即可

/** * 读取一行命令*/static uint32_t shell_read_line(void){    uint8_t ch;    uint32_t count;    /* 初始打印sh> */    if(s_cmd_buf_au8[0]=='\r')    {        shell_putstring("sh>\r\n");        s_cmd_buf_au8[0] = 0;    }
/* 非阻塞读取一个字符 */ if(shell_getchar(&ch) !=0 ) { return 0; }
/* 遇到除了退格之外的不可打印字符,则认为收到一行命令 * 退格需要单独处理,需要删除一个字符 */ if((ch == '\r' || ch == '\n' || ch < ' ' || ch > '~') && (ch != '\b')) { if(s_cmd_buf_index_u32==0) { /* 缓冲区没有数据就收到了非打印字符串,则打印提示sh> */ shell_putstring("sh>\r\n"); } else { /* 收到了非打印字符,且缓冲区有数据则认为收到了一行 * 返回缓冲区数据长度,并清零计数,打印回车换行 * 并且添加结束符0 */ count = s_cmd_buf_index_u32; s_cmd_buf_au8[s_cmd_buf_index_u32]=0; s_cmd_buf_index_u32 =0; shell_putstring("\r\n"); return count; } } else { if(ch == '\b') { /* 退格处理,注意只有有数据才会删除一个字符,添加结束符 */ if(s_cmd_buf_index_u32 != 0) { s_cmd_buf_index_u32--; shell_putchar('\b'); shell_putchar(' '); shell_putchar('\b'); s_cmd_buf_au8[s_cmd_buf_index_u32]= '\0'; } } else { /* 可打印字符,添加到缓冲区 * 如果数据量已经到了缓冲区大小-1,则也认为是一行命令 * -1是保证最后有结束符0空间 */ if(s_enableecho_u8 != 0) { shell_putchar(ch); } s_cmd_buf_au8[s_cmd_buf_index_u32++] = ch; if(s_cmd_buf_index_u32>=(sizeof(s_cmd_buf_au8)-1)) { count = s_cmd_buf_index_u32; s_cmd_buf_au8[s_cmd_buf_index_u32]=0; s_cmd_buf_index_u32 =0; shell_putstring("\r\n"); return count; } } } return 0;}

代码量很少,直接看源码即可

shell.c/h为核心代码,无需修改

shell_func.c/h为命令实现,需要自己额添加

shell.c

#include #include "shell.h"
shell_read_pf s_input_pf = 0; /* 输入接口指针 */shell_write_pf s_output_pf = 0; /* 输出接口指针 */shell_cmd_cfg* s_cmd_cfg_pst = 0; /* 命令列表指针 */uint8_t s_enableecho_u8 = 0; /* 是否使能echo标志 */static uint8_t s_cmd_buf_au8[SHELL_CMD_LEN]="\r"; /* 命令缓冲区 */static uint32_t s_cmd_buf_index_u32 = 0; /* 当前命令缓冲区中字符数 */
/** * 输出字符接口*/static void shell_putchar(uint8_t val){ uint8_t tmp; if(s_output_pf != 0) { tmp = val; s_output_pf(&tmp, 1); }}
/** * 输出字符串接口*/static void shell_putstring(char* str){ uint32_t len = 0; uint8_t*p = (uint8_t*)str; while(*str++) { len++; } s_output_pf(p, len);}
/** * 读字符接口*/static int shell_getchar(uint8_t *data){ if(s_input_pf == 0) { return -1; } if(0 == s_input_pf(data, 1)) { return -1; } else { return 0; }}
/** * 判断命令字符串的长度 * 命令字符串不能有空格*/static uint32_t shell_cmd_len(uint8_t *cmd){ uint8_t *p = cmd; uint32_t len = 0; while((*p != ' ') && (*p != 0)) { p++; len++; } return len;}
/** * 判断两个字符串是否相等,相等返回0*/static int shell_cmd_check(uint8_t *cmd, uint8_t *str){ uint32_t len1 = shell_cmd_len(cmd); uint32_t len2 = shell_cmd_len(str); if(len1 != len2) { return -1; } for(uint32_t i=0; i { if(*cmd++ != *str++) { return -1; } } return 0;}
/** * 读取一行命令*/static uint32_t shell_read_line(void){ uint8_t ch; uint32_t count; /* 初始打印sh> */ if(s_cmd_buf_au8[0]=='\r') { shell_putstring("sh>\r\n"); s_cmd_buf_au8[0] = 0; }
/* 非阻塞读取一个字符 */ if(shell_getchar(&ch) !=0 ) { return 0; }
/* 遇到除了退格之外的不可打印字符,则认为收到一行命令 * 退格需要单独处理,需要删除一个字符 */ if((ch == '\r' || ch == '\n' || ch < ' ' || ch > '~') && (ch != '\b')) { if(s_cmd_buf_index_u32==0) { /* 缓冲区没有数据就收到了非打印字符串,则打印提示sh> */ shell_putstring("sh>\r\n"); } else { /* 收到了非打印字符,且缓冲区有数据则认为收到了一行 * 返回缓冲区数据长度,并清零计数,打印回车换行 * 并且添加结束符0 */ count = s_cmd_buf_index_u32; s_cmd_buf_au8[s_cmd_buf_index_u32]=0; s_cmd_buf_index_u32 =0; shell_putstring("\r\n"); return count; } } else { if(ch == '\b') { /* 退格处理,注意只有有数据才会删除一个字符,添加结束符 */ if(s_cmd_buf_index_u32 != 0) { s_cmd_buf_index_u32--; shell_putchar('\b'); shell_putchar(' '); shell_putchar('\b'); s_cmd_buf_au8[s_cmd_buf_index_u32]= '\0'; } } else { /* 可打印字符,添加到缓冲区 * 如果数据量已经到了缓冲区大小-1,则也认为是一行命令 * -1是保证最后有结束符0空间 */ if(s_enableecho_u8 != 0) { shell_putchar(ch); } s_cmd_buf_au8[s_cmd_buf_index_u32++] = ch; if(s_cmd_buf_index_u32>=(sizeof(s_cmd_buf_au8)-1)) { count = s_cmd_buf_index_u32; s_cmd_buf_au8[s_cmd_buf_index_u32]=0; s_cmd_buf_index_u32 =0; shell_putstring("\r\n"); return count; } } } return 0;}
/** * 搜寻命令列表处理命令*/static int shell_exec_cmdlist(uint8_t* cmd){ int i; if(s_cmd_cfg_pst == 0) { return -1; } for (i=0; s_cmd_cfg_pst[i].name != 0; i++) { if (shell_cmd_check(cmd, s_cmd_cfg_pst[i].name) == 0) { s_cmd_cfg_pst[i].func(cmd); return 0; } } if(s_cmd_cfg_pst[i].name == 0) { shell_putstring("unkown command\r\n"); return -1; } return 0;}
/** * 对外接口,周期执行*/void shell_exec(void){ if(shell_read_line() > 0) { shell_exec_cmdlist(s_cmd_buf_au8); }}
/** * 对外接口,初始化配置接口*/void shell_set_itf(shell_read_pf input, shell_write_pf output, shell_cmd_cfg* cmd_list, uint8_t enableecho){ s_input_pf = input; s_output_pf = output; s_cmd_cfg_pst = cmd_list; s_enableecho_u8 = enableecho;}

shell.h

#ifndef SHELL_H#define SHELL_H
#ifdef __cplusplusextern "C" {#endif
#include
#define SHELL_CMD_LEN 64 /**< 命令缓冲区大小 */
typedef void (*shell_command_pf)(uint8_t *); /**< 命令回调函数 */typedef uint32_t (*shell_read_pf)(uint8_t *buff, uint32_t len); /**< 底层收接口 */typedef void (*shell_write_pf)(uint8_t *buff, uint32_t len); /**< 底层发接口 */
/** * \struct shell_cmd_cfg * 命令信息*/typedef struct{ uint8_t * name; /**< 命令字符串 */ shell_command_pf func; /**< 命令回调函数 */ uint8_t * helpstr; /**< 命令帮助信息 */ }shell_cmd_cfg;
/** * \fn shell_exec * 周期调用该函数,读取底层输入,并判断是否有命令进行处理 * 非阻塞*/void shell_exec(void);
/** * \fn shell_set_itf * 设置底层输入输出接口,以及命令列表 * 调用shell_exec_shellcmd之前,需要先调用该接口初始化 * \param[in] input \ref shell_read_pf 输入接口 * \param[in] output \ref shell_write_pf 输出接口 * \param[in] cmd_list \ref shell_cmd_cfg 命令列表 * \param[in] enableecho 0:不使能回显, 其他值:使能回显*/void shell_set_itf(shell_read_pf input, shell_write_pf output, shell_cmd_cfg* cmd_list, uint8_t enableecho);
#ifdef __cplusplus}#endif
#endif

shell_func.c

#include #include #include #include 
#include "shell.h"#include "shell_func.h"#include "xmodem.h"#include "uart_api.h"#include "iot_flash.h"
static void helpfunc(uint8_t* param);static void rxmemfunc(uint8_t* param);static void sxmemfunc(uint8_t* param);static void rxflashfunc(uint8_t* param);static void sxflashfunc(uint8_t* param);static void uarttestfunc(uint8_t* param);
/** * 最后一行必须为0,用于结束判断*/const shell_cmd_cfg g_shell_cmd_list_ast[ ] = { { (uint8_t*)"help", helpfunc, (uint8_t*)"help"}, { (uint8_t*)"rxmem", rxmemfunc, (uint8_t*)"rxmem addr len"}, { (uint8_t*)"sxmem", sxmemfunc, (uint8_t*)"sxmem addr len"}, { (uint8_t*)"rxflash", rxflashfunc, (uint8_t*)"rxflash addr len"}, { (uint8_t*)"sxflash", sxflashfunc, (uint8_t*)"sxflash addr len"}, { (uint8_t*)"uarttest", uarttestfunc, (uint8_t*)"uarttest"}, { (uint8_t*)0, 0 , 0},};
void helpfunc(uint8_t* param){ (void)param; unsigned int i; printf("\r\n"); printf("**************\r\n"); printf("* SHELL *\r\n"); printf("* V1.0 *\r\n"); printf("**************\r\n"); printf("\r\n"); for (i=0; g_shell_cmd_list_ast[i].name != 0; i++) { printf("%02d.",i); printf("%-16s",g_shell_cmd_list_ast[i].name); printf("%s\r\n",g_shell_cmd_list_ast[i].helpstr); }}
uint8_t rxtx_buf[1029];extern uint32_t g_tick_u32;static uint32_t getms(void){ return g_tick_u32;}
static uint32_t io_read(uint8_t* buffer, uint32_t len){ return uart_api_read(buffer, len);}
static void io_read_flush(void){ uint8_t tmp; while(0 != uart_api_read(&tmp, 1));}
static uint32_t io_write(uint8_t* buffer, uint32_t len){ uart_api_write(buffer, len); return len;}
static uint32_t mem_read(uint32_t addr, uint8_t* buffer, uint32_t len){ memcpy(buffer, (uint8_t*)addr, len); return len;}
static uint32_t mem_write(uint32_t addr, uint8_t* buffer, uint32_t len){ memcpy((uint8_t*)addr, buffer, len); return len;}
static uint32_t flash_read(uint32_t addr, uint8_t* buffer, uint32_t len){ iot_flash_read(IOT_FLASH_SFC_PORT_0, addr, buffer, len); return len;}
static uint32_t flash_write(uint32_t addr, uint8_t* buffer, uint32_t len){ iot_flash_write(IOT_FLASH_SFC_PORT_0, addr, buffer, len); return len;}

void rxmemfunc(uint8_t* param){ uint32_t addr; uint32_t len; uint8_t* p = param; int res = 0; //if(3 == sscanf((const char*)param, "%*s %s %d %d", type, &addr, &len)) while(1) { if((*p > 'z') || (*p < 'a')) { break; } else { p++; } } while(1) { if(*p != ' ') { break; } else { p++; } } addr = atoi((const char*)p); while(1) { if((*p > '9') || (*p < '0')) { break; } else { p++; } } while(1) { if(*p != ' ') { break; } else { p++; } } len = atoi((const char*)p);
xmodem_cfg_st cfg= { .buffer = rxtx_buf, .crccheck = 1, .getms = getms, .io_read = io_read, .io_read_flush = io_read_flush, .io_write = io_write, .start_timeout = 60, .packet_timeout = 1000, .ack_timeout = 1000, .mem_write = mem_write, .addr = addr, .totallen = len, }; xmodem_init_rx(&cfg); while((res = xmodem_rx()) == 0); printf("res:%d\r\n",res);}
void sxmemfunc(uint8_t* param){ uint32_t addr; uint32_t len; uint8_t* p = param; int res = 0; //if(3 == sscanf((const char*)param, "%*s %s %d %d", type, &addr, &len)) while(1) { if((*p > 'z') || (*p < 'a')) { break; } else { p++; } } while(1) { if(*p != ' ') { break; } else { p++; } } addr = atoi((const char*)p); while(1) { if((*p > '9') || (*p < '0')) { break; } else { p++; } } while(1) { if(*p != ' ') { break; } else { p++; } } len = atoi((const char*)p);
xmodem_cfg_st cfg= { .buffer = rxtx_buf, .plen = 1024, .getms = getms, .io_read = io_read, .io_read_flush = io_read_flush, .io_write = io_write, .start_timeout = 60, .packet_timeout = 1000, .ack_timeout = 1000, .mem_read = mem_read, .addr = addr, .totallen = len, }; xmodem_init_tx(&cfg); while((res = xmodem_tx()) == 0); printf("res:%d\r\n",res);}

void rxflashfunc(uint8_t* param){ uint32_t addr; uint32_t len; uint8_t* p = param; int res = 0; //if(3 == sscanf((const char*)param, "%*s %s %d %d", type, &addr, &len)) while(1) { if((*p > 'z') || (*p < 'a')) { break; } else { p++; } } while(1) { if(*p != ' ') { break; } else { p++; } } addr = atoi((const char*)p); while(1) { if((*p > '9') || (*p < '0')) { break; } else { p++; } } while(1) { if(*p != ' ') { break; } else { p++; } } len = atoi((const char*)p);
xmodem_cfg_st cfg= { .buffer = rxtx_buf, .crccheck = 1, .getms = getms, .io_read = io_read, .io_read_flush = io_read_flush, .io_write = io_write, .start_timeout = 60, .packet_timeout = 1000, .ack_timeout = 1000, .mem_write = flash_write, .addr = addr, .totallen = len, }; xmodem_init_rx(&cfg); while((res = xmodem_rx()) == 0); printf("res:%d\r\n",res);}
void sxflashfunc(uint8_t* param){ uint32_t addr; uint32_t len; uint8_t* p = param; int res = 0; //if(3 == sscanf((const char*)param, "%*s %s %d %d", type, &addr, &len)) while(1) { if((*p > 'z') || (*p < 'a')) { break; } else { p++; } } while(1) { if(*p != ' ') { break; } else { p++; } } addr = atoi((const char*)p); while(1) { if((*p > '9') || (*p < '0')) { break; } else { p++; } } while(1) { if(*p != ' ') { break; } else { p++; } } len = atoi((const char*)p);
xmodem_cfg_st cfg= { .buffer = rxtx_buf, .plen = 1024, .getms = getms, .io_read = io_read, .io_read_flush = io_read_flush, .io_write = io_write, .start_timeout = 60, .packet_timeout = 1000, .ack_timeout = 1000, .mem_read = flash_read, .addr = addr, .totallen = len, }; xmodem_init_tx(&cfg); while((res = xmodem_tx()) == 0); printf("res:%d\r\n",res);}
void uarttestfunc(uint8_t* param){ (void)param; uint8_t tmp[64]; uint32_t len; uint32_t total = 0; while(1) { while(0 != (len = uart_api_read(tmp, sizeof(tmp)))) { uart_api_write(tmp, len); total+=len; } if(total >= 10*1024) { break; } }}

shell_func.h

#ifndef SHELL_FUNC_H#define SHELL_FUNC_H
#include
#ifdef __cplusplus extern "C" {#endif extern const shell_cmd_cfg g_shell_cmd_list_ast[ ]; #ifdef __cplusplus}#endif
#endif

三. 测试

添加命令行只需要在shell_func.c中先申明

实现函数,比如

static void helpfunc(uint8_t* param);

然后在数组g_shell_cmd_list_ast中添加一行

用于指定命令行字符和对应的实现函数,以及帮助字符串。

比如

{ (uint8_t*)"help",         helpfunc,         (uint8_t*)"help"},

然后实现函数

void helpfunc(uint8_t* param){  (void)param;  unsigned int i;  printf("\r\n");  printf("**************\r\n");  printf("*   SHELL    *\r\n");  printf("*   V1.0     *\r\n");  printf("**************\r\n");  printf("\r\n");  for (i=0; g_shell_cmd_list_ast[i].name != 0; i++)  {      printf("%02d.",i);      printf("%-16s",g_shell_cmd_list_ast[i].name);      printf("%s\r\n",g_shell_cmd_list_ast[i].helpstr);  }}

然后初始化接口,执行

  uart_bsp_init(115200);  shell_set_itf(uart_api_read, uart_api_write, (shell_cmd_cfg*)g_shell_cmd_list_ast, 1);  while(1)  {      shell_exec();  }

此时就可以输入对应命令字符串回车来执行对应的函数,比如

 

四. 总结

以上以最简单的方式,实现了命令行shell。代码量和占用ram非常小,可以快速嵌入资源非常受限的开发平台。

Shell.cshell.h完全可移植,无需任何修改。

只需初始化shell_set_itf设置对应的底层接口,然后shell_func.c中添加对应的命令实现即可。

非阻塞方式实现,方便应用。

以最简形式实现,支持退格,但是不支持历史命令等,但是可以快速的修改代码实现。

命令行以数组静态添加,而不是动态添加和使用编译器的扩展将代码放置于指定段的方式来添加,是因为考虑可移植性和简单性。也可以快速修改使用后者。

有很多人总是在感慨为什么嵌入式领域热衷于造轮子,这是由于其特定的需求决定的,在嵌入式领域有诸多前提条件的限制,比如资源就是一个必须要考虑的前提,所以很难一个轮子适应所有路况。比如在8位单片机上几kram,十几krom,这时候也要使用比如shell命令行,那么就不可能使用大而全的框架,则存储资源,运行占用CPU等等都会无法满足。。甚至编译器都是专用针对嵌入式场景的,可能都不能用各种花哨的处理方式编译器扩展等(比如获取段地址,大小,指定代码位于某个段),另外在嵌入式领域编译器相关的扩展也会影响可移植性,其实不建议过多使用。

正是因为有这么多的限制所以在嵌入式领域才会有这么的多需要定制开发,重复造轮子的事,而积累自己的轮子,积累自己的小代码库,组件库,则是嵌入式高手积累经验的一条路径。积累了足够多的自己的轮子,将来面对需求就能够得心应手,才能快速开发出不同需求的轮子


评论
  • 根据环洋市场咨询(Global Info Research)项目团队最新调研,预计2030年全球无人机锂电池产值达到2457百万美元,2024-2030年期间年复合增长率CAGR为9.6%。 无人机锂电池是无人机动力系统中存储并释放能量的部分。无人机使用的动力电池,大多数是锂聚合物电池,相较其他电池,锂聚合物电池具有较高的能量密度,较长寿命,同时也具有良好的放电特性和安全性。 全球无人机锂电池核心厂商有宁德新能源科技、欣旺达、鹏辉能源、深圳格瑞普和EaglePicher等,前五大厂商占有全球
    GIRtina 2025-01-07 11:02 122浏览
  • 本文介绍编译Android13 ROOT权限固件的方法,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。关闭selinux修改此文件("+"号为修改内容)device/rockchip/common/BoardConfig.mkBOARD_BOOT_HEADER_VERSION ?= 2BOARD_MKBOOTIMG_ARGS :=BOARD_PREBUILT_DTB
    Industio_触觉智能 2025-01-08 00:06 92浏览
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 223浏览
  • 大模型的赋能是指利用大型机器学习模型(如深度学习模型)来增强或改进各种应用和服务。这种技术在许多领域都显示出了巨大的潜力,包括但不限于以下几个方面: 1. 企业服务:大模型可以用于构建智能客服系统、知识库问答系统等,提升企业的服务质量和运营效率。 2. 教育服务:在教育领域,大模型被应用于个性化学习、智能辅导、作业批改等,帮助教师减轻工作负担,提高教学质量。 3. 工业智能化:大模型有助于解决工业领域的复杂性和不确定性问题,尽管在认知能力方面尚未完全具备专家级的复杂决策能力。 4. 消费
    丙丁先生 2025-01-07 09:25 116浏览
  • 「他明明跟我同梯进来,为什么就是升得比我快?」许多人都有这样的疑问:明明就战绩也不比隔壁同事差,升迁之路却比别人苦。其实,之间的差异就在于「领导力」。並非必须当管理者才需要「领导力」,而是散发领导力特质的人,才更容易被晓明。许多领导力和特质,都可以通过努力和学习获得,因此就算不是天生的领导者,也能成为一个具备领导魅力的人,进而被老板看见,向你伸出升迁的橘子枝。领导力是什么?领导力是一种能力或特质,甚至可以说是一种「影响力」。好的领导者通常具备影响和鼓励他人的能力,并导引他们朝着共同的目标和愿景前
    优思学院 2025-01-08 14:54 61浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 164浏览
  • By Toradex 秦海1). 简介嵌入式平台设备基于Yocto Linux 在开发后期量产前期,为了安全以及提高启动速度等考虑,希望将 ARM 处理器平台的 Debug Console 输出关闭,本文就基于 NXP i.MX8MP ARM 处理器平台来演示相关流程。 本文所示例的平台来自于 Toradex Verdin i.MX8MP 嵌入式平台。  2. 准备a). Verdin i.MX8MP ARM核心版配合Dahlia载板并
    hai.qin_651820742 2025-01-07 14:52 108浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 202浏览
  • 故障现象一辆2017款东风风神AX7车,搭载DFMA14T发动机,累计行驶里程约为13.7万km。该车冷起动后怠速运转正常,热机后怠速运转不稳,组合仪表上的发动机转速表指针上下轻微抖动。 故障诊断 用故障检测仪检测,发动机控制单元中无故障代码存储;读取发动机数据流,发现进气歧管绝对压力波动明显,有时能达到69 kPa,明显偏高,推断可能的原因有:进气系统漏气;进气歧管绝对压力传感器信号失真;发动机机械故障。首先从节气门处打烟雾,没有发现进气管周围有漏气的地方;接着拔下进气管上的两个真空
    虹科Pico汽车示波器 2025-01-08 16:51 70浏览
  •  在全球能源结构加速向清洁、可再生方向转型的今天,风力发电作为一种绿色能源,已成为各国新能源发展的重要组成部分。然而,风力发电系统在复杂的环境中长时间运行,对系统的安全性、稳定性和抗干扰能力提出了极高要求。光耦(光电耦合器)作为一种电气隔离与信号传输器件,凭借其优秀的隔离保护性能和信号传输能力,已成为风力发电系统中不可或缺的关键组件。 风力发电系统对隔离与控制的需求风力发电系统中,包括发电机、变流器、变压器和控制系统等多个部分,通常工作在高压、大功率的环境中。光耦在这里扮演了
    晶台光耦 2025-01-08 16:03 58浏览
  • 村田是目前全球量产硅电容的领先企业,其在2016年收购了法国IPDiA头部硅电容器公司,并于2023年6月宣布投资约100亿日元将硅电容产能提升两倍。以下内容主要来自村田官网信息整理,村田高密度硅电容器采用半导体MOS工艺开发,并使用3D结构来大幅增加电极表面,因此在给定的占位面积内增加了静电容量。村田的硅技术以嵌入非结晶基板的单片结构为基础(单层MIM和多层MIM—MIM是指金属 / 绝缘体/ 金属) 村田硅电容采用先进3D拓扑结构在100um内,使开发的有效静电容量面积相当于80个
    知白 2025-01-07 15:02 141浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦