DIY:制作一个语音识别的空调遥控器
夏天到了,空调简直是我们的救命神器,但是对于丢三落四的我,总是因为找不到遥控器而烦恼,所以花了一天一夜的时间动手用单片机做了一个语音识别空调遥控器,把它24小时放在空调底下,从此再也不怕遥控器找不到了~由于除了遥控器最基本的功能以外,甚至还可以加上自己定义的功能,比如循环开半小时再关半小时,用来省电;或者根据环境温度调节空调温度等等等等~
效果图:
1 红外遥控器原理
红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。
红外遥控的发射电路是采用红外发光二极管来发出经过调制的红外光波;红外接收电路由红外接收二极管、三极管或硅光电池组成,它们将红外发射器发射的红外光转换为相应的电信号,再送后置放大器。
发射机一般由指令键(或操作杆)、指令编码系统、调制电路、驱动电路、发射电路等几部分组成。当按下指令键或推动操作杆时,指令编码电路产生所需的指令编码信号,指令编码信号对载波进行调制,再由驱动电路进行功率放大后由发射电路向外发射经调制定的指令编码信号。
接收电路一般由接收电路、放大电路、调制电路、指令译码电路、驱动电路、执行电路(机构)等几部分组成。接收电路将发射器发出的已调制的编码指令信号接收下来,并进行放大后送解调电路,解调电路将已调制的指令编码信号解调出来,即还原为编码信号。指令译码器将编码指令信号进行译码,最后由驱动电路来驱动执行电路实现各种指令的操作控制(机构)。
红外遥控的编码目前广泛使用的是:NEC Protocol 的 PWM(脉冲宽度调制)和 Philips RC-5 Protocol 的 PPM(脉冲位置调制)。这里我抓取了我自己奥克斯空调遥控器的指令波形,发现使用的是NEC协议脉宽调制。
NEC 码的位定义:一个脉冲对应 560us 的连续载波,一个逻辑 1 传输需要 2.25ms(560us脉冲+1680us 低电平),一个逻辑 0 的传输需要 1.125ms(560us 脉冲+560us 低电平)。而遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到的信号为:逻辑 1 应该是 560us 低+1680us 高,逻辑 0 应该是 560us 低+560us 高。数据均采用8bit传输方式,低位在前高位在后。
需要注意的是,这里接收器解码出的高电平,实际上发射器是灭掉状态;低电平才是发射器亮起状态。而且发射器端是要用38KHz的载波驱动的,并不是简单的高电平驱动。这一点一定要注意。
2 红外遥控器的制作流程
2.1 遥控器指令解码
解码遥控器指令不是必须的,但这一步可以加深对协议的理解,方便后续代码的编写。如果你在网上能找到你所复制的遥控器的协议编码方式,可以省略这一步。
首先,用逻辑分析仪抓取红外接收器解调出的信号,大体了解一下协议格式。
比如上图中红色长方形内的数据为0b11110110,因为是低位在前,所以这个数据为0x6F,同理黄色长方形内的数据为0xE0。这个步骤主要是为了后面用单片机进行解码时,对结果正确性进行校验。
根据不同功能下的指令差异,可以分析出每个字节的每一位代表的含义。比如我的AUX空调遥控器的命令分析如下。主流的空调品牌的协议应该在网上可以找得到。
2.2 单片机解码代码编写
我的红外接收器和发射器是淘宝随便买的,单片机用的是华大的HC32L130,当然这些都是可以自己选择的,成本越低越好。因为驱动红外发射器用的载波只需要38KHz,对单片机的时钟频率要求也不是很高。
考虑到由于协议为PWM调制,所以需要分析占空比来确定每一位代表的含义,但实际上只需要看高电平的时间就可以确定这一位的含义,所以采用PWM捕获来捕获上升沿,捕获到之后马上把捕获类型改为下降沿捕获。这样两次捕获之间的计数器的值除以时钟频率就是高电平的时间。除了SYNC信号外,每8个bit为一个字节数据,数据存放在一个数组buffer中。由于AUX遥控器为13byte一条指令,所以接收到13byte之后在主函数里把接收到的数据打印出来并重置buffer。代码示例如下:
初始化函数:
void PwmCaptureInit(void)
{stc_pcacfg_t PcaInitStruct;stc_gpio_cfg_t GpioInitStruct;Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);Sysctrl_SetPeripheralGate(SysctrlPeripheralPca, TRUE); //PA07设置为PCA_CH1GpioInitStruct.enDrv = GpioDrvH;GpioInitStruct.enDir = GpioDirIn;Gpio_Init(GpioPortA, GpioPin7, &GpioInitStruct);Gpio_SetAfMode(GpioPortA,GpioPin7,GpioAf2);PcaInitStruct.pca_clksrc = PcaPclkdiv32; //32MHz / 32 = 1MHzPcaInitStruct.pca_cidl = FALSE;PcaInitStruct.pca_ecom = PcaEcomDisable; PcaInitStruct.pca_capp = PcaCappEnable; //上升沿捕获PcaInitStruct.pca_capn = PcaCapnDisable; //禁止下降沿捕获PcaInitStruct.pca_mat = PcaMatDisable; PcaInitStruct.pca_tog = PcaTogDisable; PcaInitStruct.pca_pwm = PcaPwm8bitDisable; PcaInitStruct.pca_epwm = PcaEpwmDisable; Pca_M1Init(&PcaInitStruct); Pca_ClrItStatus(PcaCcf1);Pca_ConfModulexIt(PcaModule1, TRUE);EnableNvic(PCA_IRQn, IrqLevel3, TRUE);Pca_StartPca(TRUE);
}
中断服务函数:
uint8_t remoteStatus = 0;
uint16_t captureValue = 0; //下降沿时计数器的值
uint8_t captureCount = 0; //按键按下的次数
uint8_t cmdBuf[13] = { 0 };//每条命令有13bytes
uint8_t cmdNum = 0;//当前是第几条命令uint8_t bitCnt = 0;
void Pca_IRQHandler(void)
{M0P_TIM0_MODE0->M0CR &= ~1; //停止并清零定时器M0P_TIM0_MODE0->CNT = 0;if(Pca_GetItStatus(PcaCcf1) != FALSE){if(Gpio_GetInputIO(GpioPortA, GpioPin7) == TRUE)//捕获到上升沿后,标记捕获状态并切换为下降沿捕获{M0P_PCA->CCAPM1_f.CAPP = 0;M0P_PCA->CCAPM1_f.CAPN = 1; //切换为下降沿捕获Pca_SetCnt(0);remoteStatus |= 0X10;}else //捕获到下降沿,判断当前接收到的是什么指令码,并回到上升沿捕获{captureValue = Pca_GetCcap(PcaModule1);M0P_PCA->CCAPM1_f.CAPP = 1;M0P_PCA->CCAPM1_f.CAPN = 0; //切换为上升沿捕获if(remoteStatus & 0X10) //前面捕获到上升沿{if(remoteStatus & 0X80) //当前命令存在合法的SYNC码头{if(captureValue > 300 && captureValue < 800) //560us{cmdBuf[cmdNum] >>= 1; //接收到0,右移一位bitCnt++;}else if(captureValue > 1400 && captureValue < 1900) //1680us{cmdBuf[cmdNum] >>= 1; //接收到1,左移一位并存在最高位cmdBuf[cmdNum] |= 0x80; bitCnt++;}else if(captureValue > 2200 && captureValue < 2700) //2.5ms,按键次数增加{captureCount++; remoteStatus &= 0XF0; //清空计时器}if(bitCnt==8){bitCnt = 0;cmdNum++;}}else if(captureValue > 4200 && captureValue < 4700) //4.5ms SYNC{remoteStatus |= 0X80; //SYNC状态位置1captureCount = 0; //清除按键次数计数器}}remoteStatus &= ~0x10; //等待下一次上升沿捕获}}Pca_ClrItStatus(PcaCcf1);Pca_ClrItStatus(PcaCf);M0P_TIM0_MODE0->M0CR |= 1; //定时器重新开始计数
}
2.3 单片机编码发送代码编写
那么到这里就可以按照协议来发送我们的命令了。注意驱动红外二极管要用38KHz的载波驱动。因为这个载波频率不需要特别精准,所以我只是用软件模拟了一个方波。需要精准的PWM驱动可以用定时器/硬件PWM实现。
驱动代码:
void RemoteGpioInit(void)
{stc_gpio_cfg_t GpioInitStruct;DDL_ZERO_STRUCT(GpioInitStruct);Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);//PB9作为红外输出data脚GpioInitStruct.enDrv = GpioDrvH;GpioInitStruct.enDir = GpioDirOut;Gpio_Init(GpioPortB, GpioPin9, &GpioInitStruct);
}void IRSend_1(void)
{uint32_t cnt = 0x2C;while(cnt--) //38KHz,560us{M0P_GPIO->PBOUT ^= 0x200;__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();}
}void IRSend_0(void)
{M0P_GPIO->PBOUT &= ~0x200;delay10us(30);//延时函数不准,这里应该是560us
}
发送代码:
void RemoteSend(uint8_t* data,uint16_t len)
{REMOTE_SEND_SYNC;while(len--){RemoteSendByte(*data);data++;}REMOTE_SEND_END;
}
//低位先发
void RemoteSendByte(uint8_t data)
{int i=0;for(;i<8;i++){if(data & 0x01){REMOTE_SEND_1;}else{REMOTE_SEND_0;}data >>= 1;}
}
到此为止,红外线收发环境已经搭建完成。
2.4 语音识别模块配置
这里我选择的语音识别模块为天问TW-ASR,这个模块我用起来感觉还是很容易上手的。可以图形化编程,语音识别也比较准。在模块定制的编程工具上写好你要识别的语音,并把语音匹配成功的行为设置好(比如通过串口发送数据)。
2.5 单片机串口模块配置+红外发射
语音模块识别成功以后,通过串口将指令发送给单片机。我们用单片机接收到其发送的指令,每接收到13字节就判断接受到的指令含义,并把对应的命令通过红外二极管发送给空调,这样一套完整的语音识别遥控器方案就大功告成了!
串口初始化:
void UartInit(int baud)
{stc_uart_cfg_t stcCfg;stc_uart_baud_t stcBaud;stc_gpio_cfg_t stcGpioCfg;DDL_ZERO_STRUCT(stcGpioCfg);DDL_ZERO_STRUCT(stcCfg);DDL_ZERO_STRUCT(stcBaud);Sysctrl_SetPeripheralGate(SysctrlPeripheralUart1,TRUE); ///<使能UART1外设时钟门控开关///<UART InitstcCfg.enRunMode = UartMskMode1; ///<模式3stcCfg.enStopBit = UartMsk1bit; ///<1bit停止位stcCfg.enMmdorCk = UartMskDataOrAddr; ///<多机模式时stcCfg.stcBaud.u32Baud = baud; ///<波特率9600stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div; ///<通道采样分频配置stcCfg.stcBaud.u32Pclk = Sysctrl_GetPClkFreq(); ///</<获得外设时钟(PCLK)频率值Uart_Init(M0P_UART1, &stcCfg); ///<串口初始化///<UART中断使能Uart_ClrStatus(M0P_UART1,UartRC); ///<清接收请求Uart_ClrStatus(M0P_UART1,UartTC); ///<清接收请求Uart_EnableIrq(M0P_UART1,UartRxIrq); ///<使能串口接收中断EnableNvic(UART1_IRQn, IrqLevel2, TRUE); ///<系统中断使能Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);stcGpioCfg.enDir = GpioDirOut;Gpio_Init(GpioPortA,GpioPin2,&stcGpioCfg);Gpio_SetAfMode(GpioPortA,GpioPin2,GpioAf1); stcGpioCfg.enDir = GpioDirIn;Gpio_Init(GpioPortA,GpioPin3,&stcGpioCfg);Gpio_SetAfMode(GpioPortA,GpioPin3,GpioAf1);
}
串口中断:
char uartRecvDataBuf[20];
uint16_t uartRecvDataNum;
uint8_t uartRxFlg=0;
void Uart1_IRQHandler(void)
{if(Uart_GetStatus(M0P_UART1, UartRC)){uartRecvDataBuf[uartRecvDataNum++] = Uart_ReceiveData(M0P_UART1);uartRxFlg = 1;}Uart_ClrStatus(M0P_UART1, UartRC);
}
数据解析:
#define COMMAND_AMOUNT 36
char* commands[COMMAND_AMOUNT] =
{"TURN ON\r\n","TURN OFF\r\n", //0 1"TEMP -1\r\n","TEMP +1\r\n", //2 3"SPEED 1\r\n","SPEED 2\r\n","SPEED 3\r\n","SPEED 4\r\n","SPEED 5\r\n","SPEED AUTO\r\n", //4 5 6 7 8 9"MODE HEAT\r\n","MODE COLD\r\n","MODE AUTO\r\n", //10 11 12"WINDLR ON\r\n","WINDLR OFF\r\n","WINDUD ON\r\n","WINDUD OFF\r\n", //13 14 15 16"TIMEON 1\r\n","TIMEON 2\r\n","TIMEON 3\r\n","TIMEON 4\r\n", //17 18 19 20"TIMEOFF 0.5\r\n","TIMEOFF 1\r\n","TIMEOFF 1.5\r\n","TIMEOFF 2\r\n","TIMEOFF 2.5\r\n", //21 22 23 24 25"TIMEOFF 3\r\n","TIMEOFF 3.5\r\n","TIMEOFF 4\r\n","TIMEOFF 4.5\r\n","TIMEOFF 5\r\n", //26 27 28 29 30"TIMEOFF 5.5\r\n","TIMEOFF 6\r\n", //31 32"TIMEONOFF 1 1\r\n","TIMEONOFF 0.5 0.5\r\n","TIMEONOFF 1 0.5\r\n", //33 34 35
};
void AnalyzeCommand(void)
{int i=0;for(i=0;i<COMMAND_AMOUNT;i++){if(strcmp(uartRecvDataBuf,commands[i]) == 0)//匹配{SendCommand(i);}}
}
指令发送:
uint8_t RemoteCommand[13] = {0xc3,0x8f,0xe0,0x00,0xe0,0x00,0x20,0x00,0x80,0x00,0x00,0x45,0x41};void SendCommand(uint16_t cmdNum)
{uint16_t i = 0;uint8_t tmp = 0;switch(cmdNum){case 0: //TURN ONRemoteCommand[9] |= 1<<5;RemoteCommand[11] = 0x45;break;case 1://TURN OFFRemoteCommand[9] &= ~(1<<5);RemoteCommand[11] = 0x45;break;case 2://TEMP -1tmp = (RemoteCommand[1]>>3) - 1;RemoteCommand[1] &= ~(0xf8);RemoteCommand[1] |= tmp<<3;RemoteCommand[11] = 0x41;break;case 3://TEMP +1tmp = (RemoteCommand[1]>>3) + 1;RemoteCommand[1] &= ~(0xf8);RemoteCommand[1] |= tmp<<3;RemoteCommand[11] = 0x40;break;case 4: //SPEED 1RemoteCommand[4] &= ~(0xe0);RemoteCommand[4] |= 0x3 << 5;RemoteCommand[11] = 0x44;break;case 5:RemoteCommand[4] &= ~(0xe0);RemoteCommand[4] |= 0x2 << 5;RemoteCommand[11] = 0x44;break;case 6:RemoteCommand[4] &= ~(0xe0);RemoteCommand[4] |= 0x1 << 5;RemoteCommand[11] = 0x44;break;case 7:RemoteCommand[4] &= ~(0xe0);RemoteCommand[4] |= 0x6 << 5;RemoteCommand[11] = 0x44;break;case 8:RemoteCommand[4] &= ~(0xe0);RemoteCommand[4] |= 0x5 << 5;RemoteCommand[11] = 0x44;break;case 9: //SPEED AUTORemoteCommand[4] &= ~(0xe0);RemoteCommand[4] |= 0x7 << 5;RemoteCommand[11] = 0x44;break;case 10: //MODE HEATRemoteCommand[6] &= ~(0xe0);RemoteCommand[6] |= 0x80;RemoteCommand[11] = 0x46;break;case 11: //MODE COLDRemoteCommand[6] &= ~(0xe0);RemoteCommand[6] |= 0x20;RemoteCommand[11] = 0x46;break;case 12: //MODE AUTORemoteCommand[6] &= ~0xe0;RemoteCommand[6] |= 0x00;RemoteCommand[11] = 0x46;break;case 13: //WINDLR ONRemoteCommand[2] &= ~0xe0;RemoteCommand[11] = 0x43;break;case 14:RemoteCommand[2] |= 0xe0;RemoteCommand[11] = 0x43;break;case 15://WINDUD ONRemoteCommand[1] &= ~0x3;RemoteCommand[11] = 0x42;break;case 16:RemoteCommand[1] |= 0x3;RemoteCommand[11] = 0x42;break;case 17: //TIMEON 1 2 3 4case 18:case 19:case 20:RemoteCommand[5] &= ~0x1F;RemoteCommand[4] &= ~0x1F;RemoteCommand[4] |= cmdNum - 16;RemoteCommand[11] = 0x4d;break;case 21: case 23: case 25: case 27: case 29: case 31: //TIMEOFF 0.5 1.5 ...5.5RemoteCommand[5] |= 0x1F; //0.5hRemoteCommand[4] &= ~0x1F;RemoteCommand[4] |= (cmdNum - 21) / 2;RemoteCommand[11] = 0x4d;break;case 22: case 24: case 26: case 28: case 30: case 32: //TIMEOFF 1 2 3 4 5 6RemoteCommand[5] &= ~0x1F;RemoteCommand[4] &= ~0x1F;RemoteCommand[4] |= (cmdNum - 20) / 2;RemoteCommand[11] = 0x4d;break;case 33:case 34:case 35:default:return;}RemoteCommand[12] = 0;for(i = 0; i < 12; i++)RemoteCommand[12] += RemoteCommand[i];RemoteSend(RemoteCommand, 13);
}
到此为止,一个完整的语音识别空调遥控器就完成了!需要更多资料可以关注公众号:嵌入式付呱呱,后台留言即可!
DIY:制作一个语音识别的空调遥控器相关推荐
- DIY制作一个所属自己的输入法皮肤
输入法是作为电脑办公必备的软件,但大部分人都不会用电脑自带的输入法,基本都是用第三方软件.其一是功能配置比较多,其二是有很多好看的皮肤.今天我来说说怎么做一个属于你自己的皮肤! 准备 搜狗输入法(方便 ...
- 树莓派学习手记——制作一个空调遥控器(红外接收、发射的实现)
CSDN广告邮件太多了,邮箱已经屏蔽了CSDN,留言请转SegmentFault:https://segmentfault.com/a/1190000014135418 使用树莓派搭配红外管,进行接收 ...
- 树莓派学习手记——制作一个空调遥控器(红外接收、发射的实现) 1
使用树莓派搭配红外管,进行接收.发射红外信号是很方便的,同时红外信号也有很广泛的用途.这次我们将总结使用树莓派制作一个空调红外遥控器的过程. 准备工具 红外接收管(参考型号HS0038B) 红外发射管 ...
- 【DIY】用树莓派制作一个便携游戏主机
用树莓派制作一个便携游戏主机 本文首发于酷安,现搬运到CSDN. 转载请标明出处 折叠状态 Q:这又是什么鬼? A:树莓派+LAKKA+显示屏+充电宝+披萨盒+游戏手柄=超便携简(jian)易(lou ...
- 使用OLAMI SDK和讯飞语音合成制作一个语音回复的短信小助手
现代人的生活越来越离不开手机,但我们总会遇到一些时候不方便用手去操作,比如开车,玩游戏的时候.智能语音时代这种情况有了新的解决方案.本文介绍了一个使用OLAMI Android SDK进行语音识别和理 ...
- DIY制作高精度低温漂电阻箱0.1~9.9GΩ(1)-- 项目介绍
最新工作需要,DIY制作了一款高精度低温漂的电阻箱.想起在大学电子设计大赛时,也动手制作过一个多档位电阻箱.这次将参数进行提升优化,电阻的选型真是下了超大血本了,电路上没有什么复杂原理,电阻串联相加即 ...
- u盘怎么样在w ndows7上使用,Windows7安装U盘 DIY 制作全攻略
Win7之家( www.win7china.com):Windows7安装U盘 DIY 制作全攻略 本文上手度:适合对BIOS设置.ISO镜像软件不熟悉的用户,高手请不用口水,请直接去软媒论坛Win7 ...
- 55节开源巨献,教你制作一个智能无线电应答平台
<如何制作一个智能无线电应答平台> 作者:BG7EJL 项目背景 目前市面上基于U/V ...
- 分享 使用普通笔记本电脑,DIY制作合成混音MTV视频,并上传分享的步骤
分享 使用普通笔记本电脑,DIY制作合成混音MTV视频,并上传分享的步骤 设备:笔记本一台(自带麦克风),耳机一个(必须) 1. 首先选好你要唱的歌曲,多练习几次 2. 用谷歌搜索一下,下载这首歌的伴 ...
最新文章
- Http 协议详解笔记
- python学起来难吗-python学起来难吗
- 【RecyclerView】 四、RecyclerView 布局 ( 网格局管理器 GridLayoutManager )
- Spring BeanFactory实例化Bean的过程
- 30 年开源老兵,10 年躬耕 OpenStack,开源 1000 万行核心代码
- ARM处理器详解(2)-Cortex R系列
- c语言 静态链表插入排序,数据结构 - 表插入排序 具体解释 及 代码(C++)
- Shadow Map在DirectX9.0 SDK Sample 的实现方法
- day69-oracle 22-DBCA
- OPC UA 统一架构学习4
- Excel高级应用专题-数组公式
- Docker(从入门到部署微服务集群)
- word一打字就有下划线_[word文档打字有下划线]下划线粗细不一致的原因:控制Word下划线与文字的距离...
- php 手写签批 手机办公_好签原笔迹手写签批SDK
- html5微杂志源码,H5制作又一利器:分分钟制作一个H5页面
- 看集装箱号码识别技术如何解决港口拥堵
- [小记] 微信小程序 - 人脸识别前端(一)初体验
- vue项目中常用的优秀插件库
- 清华NLP组论文清单:全方位覆盖自然语言处理12大领域
- 以太坊Go-ethereum源码分析之启动流程
热门文章
- Android动画特效第二弹——QQ聊天彩蛋蹦蹦哒
- 彩色matlab代码拷贝到word研究,matlab编辑器合并_彩色MATLAB代码拷贝到WORD研究
- 鸿蒙麒麟食华为的什么系统,你好,鸿蒙!华为自研系统真的来了,还有麒麟、朱雀.........
- 【Android】使用MediaExtractor、MediaMuxer去掉视频文件中的音频数据
- Axure 9 实战案例,动态面板的应用 5,官网首页自动轮播广告
- 计算机上播放时没声音什么故障,电脑经常在播放电影(音乐等)时突然没有声音...
- 搜狗输入法皮肤编辑器(“用户账户不存在: 解压缩失败 皮肤解压失败:skin.ini不存在……”)
- 10G万兆光模块怎么用?组网方案详解
- css3切角文本框_CSS3如何实现4个切角
- 串口虚拟工具(VSPD)的使用方法