【DigiKey创意大赛】基于STM32H7B3I-DK的智能家居助手+Shell命令行交互遥控测试
[复制链接]
七.Shell命令行交互
前面我们已经实现了任意红外信号的发送,这样就可以发送任意的红外遥控心好累。
这时已经具备了一个万用红外遥控的雏形,现在开始我们来增加交互功能,实现简单的shell命令行操作。这样我们就可以通过PC或者手机蓝牙串口透传和板子交互,控制板子发送红外遥控信号。
7.1 shell命令行实现
参考一个超级精简高可移植的shell命令行C实现 《https://mp.weixin.qq.com/s/XLmbJn0SKoDT1aLdxHDrbg》,这里直接贴出代码
Shell.c
#include <stdint.h>
#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<len1; 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.name != 0; i++)
{
if (shell_cmd_check(cmd, s_cmd_cfg_pst.name) == 0)
{
s_cmd_cfg_pst.func(cmd);
return 0;
}
}
if(s_cmd_cfg_pst.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 __cplusplus
extern "C" {
#endif
#include <stdint.h>
#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
Shell_func.h
#ifndef SHELL_FUNC_H
#define SHELL_FUNC_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const shell_cmd_cfg g_shell_cmd_list_ast[ ];
#ifdef __cplusplus
}
#endif
#endif
Shell_func.c
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "shell.h"
#include "shell_func.h"
#include "uart_api.h"
static void helpfunc(uint8_t* param);
/**
* 最后一行必须为0,用于结束判断
*/
const shell_cmd_cfg g_shell_cmd_list_ast[ ] =
{
{ (uint8_t*)"help", helpfunc, (uint8_t*)"help"},
{ (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.name != 0; i++)
{
printf("%02d.",i);
printf("%-16s",g_shell_cmd_list_ast.name);
printf("%s\r\n",g_shell_cmd_list_ast.helpstr);
}
}
7.2 万用遥控-遥控电视开关测试
我们使用前面设计的红外模块接收电视遥控的开关机信号,采集到信号如下
对应如下数据
iostate_t s_iostate[2][40]=
{
{
{0,3000}, /* 0 - 3mS 1000 25uS*/
{1,3000}, /* 1 - 3ms */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,2500}, /* 1 -2,5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,2500}, /* 1 -2.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,2500}, /* 1 -2.5mS */
{0,500}, /* 0- 500uS */
{1,2500}, /* 1 -2.5mS */
{0,500}, /* 0- 500uS */
{1,4000}, /* 1 -4mS */
{0,500}, /* 0- 500uS */
{1,60000}, /* 1 -1000mS */
{0Xff,0}
},
{
{0,3000}, /* 0 - 3mS 1000 25uS*/
{1,3000}, /* 1 - 3ms */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,2500}, /* 1 -2,5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,2500}, /* 1 -2.5mS */
{0,500}, /* 0- 500uS */
{1,1500}, /* 1 -1.5mS */
{0,500}, /* 0- 500uS */
{1,2500}, /* 1 -2.5mS */
{0,500}, /* 0- 500uS */
{1,2500}, /* 1 -2.5mS */
{0,500}, /* 0- 500uS */
{1,4000}, /* 1 -4mS */
{0,500}, /* 0- 500uS */
{1,22500}, /* 1 -22.5mS */
{0Xff,0}
}
};
添加命令行
shell_func.c中
#include "ir.h"
g_shell_cmd_list_ast中添加两行
{ (uint8_t*)"pwron", pwronfunc, (uint8_t*)"pwron"},
{ (uint8_t*)"pwroff", pwrofffunc, (uint8_t*)"pwroff"},
开始申明函数
static void pwronfunc(uint8_t* param);
static void pwrofffunc(uint8_t* param);
实现
void pwronfunc(uint8_t* param)
{
for(int i=0;i<3;i++)
{
ir_send(0);
}
}
void pwrofffunc(uint8_t* param)
{
for(int i=0;i<3;i++)
{
ir_send(1);
}
}
可以看到添加了如下命令
输入pwron on和pwr off就可以发送对应的信号,遥控电视开关机。
详见视频
|