2021年第一篇文章,祝各位开工大吉,开学大吉,公众号也正是更名为“嵌入式实验基地”~

  本次为各位小伙伴带来的是一种非常普遍且便宜易实现的短距离无线通讯-红外通讯,电视机、空调虽无线控制方式也是五花八门了,但红外控制仍然占据着一席之地,本文从原理上介绍到最终实现控制舵机,当然不只是舵机,路铺好了,走什么车还不是自己说了算嘛,哈哈~闲话少说,开干!

完成目标

  • HAL库定时器输入捕获功能使用

  • HAL库PWM多路输出使用

  • 红外发射、接收测试

  • 红外控制PWM控制MG995 180度舵机

硬件环境

  • STM32F407ZGT6(或其他主控板)

  • 红外发射器(遥控器)、红外接收管

  • MG995 180度舵机

软件环境

  • keil5

  • cubemx

  • sscom串口调试助手(或其他)

1 红外遥通讯简介

1.1 红外信号调制解调原理

  平时所使用的红外遥控器传输的信号是经过调制过的信号,调制、解调是无线通信的经常用的通信手段,通信原理、高频电子课程上大家想必都已经很熟悉了。红外遥控器所使用的是38KHZ的载波频率,下面结合红外通讯做点简单介绍。

  调制:就是用待传送信号去控制某个高频信号的幅度、相位、频率等参量变化的过程,即用一个信号去装载另一个信号。比如我们的红外遥控信号要发送的时候,先经过38K调制,如图所示。

  原始信号就是我们要发送的一个数据“0”位或者一位数据“1”位,而所谓38K载波就是频率为38K的方波信号,调制后信号就是最终我们发射出去的波形。我们使用原始信号来控制38K载波,当信号是数据“0”的时候,38K载波毫无保留的全部发送出去,当信号是数据“1”的时候,不发送任何载波信号。

  正常来讲,如果对经过调制的信号进行解调处理,是需要很多工作的,比如,信号检测、前级放大、后级放大、滤波、解调电路等,最后解调出原始信号,但红外通信的一体化接收头HS0038B,已经把这些电路全部集成到一起了,我们只需要把这个电路接上去,就可以直接输出我们所要的基带信号了,如图2所示,下图是我实际设计验证过的电路,也是很多开源设计上的原理,比较简单,只需要很少的外围器件。

  上图所示电路,当HS0038监测到有38K的红外信号时,就会在OUT引脚输出低电平,当没有38K的时候,OUT引脚就会输出高电平。原始信号对我们来说就是纯粹的高低电平了,采集的方法也有很多,此次我们是用单片机资源,输入捕获功能来采集数据。

1.2 红外通信协议介绍

  红外通信协议也是多种多样,只介绍一种常用的编码协议,NEC协议,载波也就是上面说到的38KHZ载波,NEC协议是基于PWM(脉冲宽度调制)来传输数据的。其特征如下:

  • 8 位地址和 8 位指令长度

  • 地址和命令 2 次传输(确保可靠性)

  • PWM 脉冲宽度调制,以发射红外载波的占空比代表“0”和“1”;

  • 载波频率为 38Khz

  • 位时间为 1.125ms 或 2.25ms

  NEC 码的位定义:一个脉冲对应 560us 的连续载波:

一个逻辑 1 传输需要 2.25ms(560us脉冲+1680us 低电平),

一个逻辑 0 的传输需要 1.125ms(560us 脉冲+560us 低电平)。

  而遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到的信号为:逻辑 1 应该是 560us 低+1680us 高,逻辑 0 应该是 560us 低+560us 高。

  NEC 遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。同步码由一个 9ms 的低电平和一个 4.5ms 的高电平组成,地址码、地址反码、控制码、控制反码均是8 位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。

  下面结合我们逻辑分析仪截取的数据波形,对着协议对上面提到的协议格式进行分析,下面是按键2的波形,对应的数据为98即01100010:  从波形图中我们可以看出,显示同步码,9ms的低电平+4.5ms的高电平,紧接着是8位地址码,地址码为0,后面是地址反码,FF,在后面是数据码,刚好是98,也是我们2键按下后发送的数据,最后是数据反码。

  可以看到在数据之后,还收到了几个脉冲,这是 NEC 码规定的连发码(由 9ms 低电平+2.5m 高电平+0.56ms 低电平+97.94ms 高电平组成),如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,即连发码,可以通过统计连发码的次数来标记按键按下的长短/次数。

  至此,关于红外的一些基础知识就算啰嗦完了,没懂的可以加小飞哥好友,进群,和小伙们一起讨论哈。

2 舵机控制原理

  关于舵机的部分可以参考:舵机控制篇

3 定时器输入捕获原理

  输入捕获模式可以用来测量脉冲宽度或者测量频率。我们以测量脉宽为例,用一个简图来说明输入捕获的原理,如下图:

  假定定时器工作在向上计数模式,图中 t1~t2 时间,就是我们需要测量的高电平时间。测量方法如下:首先设置定时器通道 x 为上升沿捕获,这样, t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。这样,根据定时器的计数频率,我们就可以算出 t1~t2 的时间,从而得到高电平脉宽。

  在 t1~t2 之间,可能产生 N 次定时器溢出,这就要求我们对定时器溢出,做处理,防止高电平太长,导致数据不准确。如图所示, t1~t2之间, CNT计数的次数等于:N*ARR+CCRx2,有了这个计数次数,再乘以 CNT 的计数周期,即可得到 t2-t1 的时间长度,即高电平持续时间。这就是输入捕获的原理,简单来说就是测量高低电平的时间,进而求得脉冲频率或者数据编码格式。

3.1 硬件连接

  • MCU与红外接收管连接

MCU 红外接收
VCC(3.3V) VCC
GND GND
PB9 OUT
  • MCU与舵机连接

MCU MG995舵机
VCC(5V) VCC
GND GND
PB1、PA6、PB0、PA7 PWM输入

4 软件实现

4.1 cubemx配置

输入捕获配置

  使用PB9引脚,也即是TIM4的4通道,分频系数配置为83,计数单位1us,超时时间设置为10ms,输入捕获触发方式为上升沿触发,下面有个滤波器需要特别注意下,手册中对滤波器的介绍是:

  输入捕获 4 滤波器 IC4F[3:0],这个用来设置输入采样频率和数字滤波器长度。其中,是定时器的输入频率(TIMxCLK),一般为 84Mhz/168Mhz(看该定时器在那个总线上),而则是根据TIMx_CR4的CKD[1:0]的设置来确定的,如果CKD[1:0]设置为00,那么fDTS = fck_INT 。N 值就是滤波长度,举个简单的例子:假设 IC4F[3:0]=0011,并设置 IC4 映射到通道 4 上,且为上升沿触发,那么在捕获到上升沿的时候,再以 的频率,连续采样到 8 次通道 4 的电平,如果都是高电平,则说明却是一个有效的触发,就会触发输入捕获中断(如果开启了的话)。这样可以滤除那些高电平脉宽低于 8 个采样周期的脉冲信号,从而达到滤波的效果。这里,我们不做滤波处理,所以设置 IC1F[3:0]=0000,只要采集到上升沿,就触发捕获。

  • 4路PWM配置

  在之前的舵机控制中,使用了1路PWM输出,4路跟1路配置基本一样,小伙伴们可以参考前面文章:链接在这里。

4.2 代码实现

  本次使用的主要是定时器相关的功能,cubemx配置比较简单,配置完成后,根据上面的原理介绍,我们来编写应用层函数。

  • 红外NEC协议解码实现

  根据上面NEC编码的介绍,我们只需要通过输入捕获功能检测高低电平时间,进而计算出逻辑0和逻辑1,得到传输的数据。

  首先定义个红外接收相关的结构体:

typedef struct Remote{uint8_t  RmtSta;          //捕获状态//[7]:0,没有成功的捕获;1,成功捕获到一次.//[6]:0,还没捕获到低电平;1,已经捕获到低电平了.//[5:0]:捕获低电平后溢出的次数(对于 32 位定时器来说,1us 计数器加 1,溢出时间:4294 秒)uint16_t Dval;         //下降沿时计数器的值uint32_t RmtRec;        //红外接收到的数据          uint8_t  RmtCnt;        //按键按下的次数  }Remotepara;

   输入捕获定时器初始化代码,这部分也是cubemx自动生成的,已经进群的小伙伴会在源码地址下载源码,贴出来主要是给不方便下载的小伙伴们看,下载源码的直接跳过哈。

/* TIM4 init function */
void MX_TIM4_Init(void)
{TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};TIM_IC_InitTypeDef sConfigIC = {0};htim4.Instance = TIM4;htim4.Init.Prescaler = 83;htim4.Init.CounterMode = TIM_COUNTERMODE_UP;htim4.Init.Period = 10000;htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim4) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK){Error_Handler();}if (HAL_TIM_IC_Init(&htim4) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK){Error_Handler();}sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;sConfigIC.ICFilter = 0;if (HAL_TIM_IC_ConfigChannel(&htim4, &sConfigIC, TIM_CHANNEL_4) != HAL_OK){Error_Handler();}}void RemoteInit()
{HAL_TIM_Base_Start_IT(&htim4);  //开启定时器4定时器中断HAL_TIM_IC_Start_IT(&htim4,TIM_CHANNEL_4);                   //开始捕获TIM4的通道4Remotepara RemoteParameters = {0};
}

   接下来的输入捕获部分是我们NEC解码的关键部分,先看代码,也是参考网上的开源代码,根据上面的NEC编码,一步步实现。

/定时器输入捕获中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)    //捕获中断发生时执行
{if(htim->Instance==TIM4){if(RDATA)                          //上升沿捕获{TIM_RESET_CAPTUREPOLARITY(&htim4,TIM_CHANNEL_4);          //一定要先清除原来的设置!!TIM_SET_CAPTUREPOLARITY(&htim4,TIM_CHANNEL_4,TIM_ICPOLARITY_FALLING);  //CC4P=1 设置为下降沿捕获__HAL_TIM_SET_COUNTER(&htim4,0);             //清空定时器值      RemoteParameters.RmtSta|=0X10;            //标记上升沿已经被捕获}else //下降沿捕获{RemoteParameters.Dval=HAL_TIM_ReadCapturedValue(&htim4,TIM_CHANNEL_4);  //读取CCR4也可以清CC4IF标志位TIM_RESET_CAPTUREPOLARITY(&htim4,TIM_CHANNEL_4);          //一定要先清除原来的设置!!TIM_SET_CAPTUREPOLARITY(&htim4,TIM_CHANNEL_4,TIM_ICPOLARITY_RISING);  //配置TIM4通道4上升沿捕获if(RemoteParameters.RmtSta&0X10)           //完成一次高电平捕获 {if(RemoteParameters.RmtSta&0X80)          //接收到了引导码{if(RemoteParameters.Dval>300&&RemoteParameters.Dval<800)   //560为标准值,560us{RemoteParameters.RmtRec<<=1;         //左移一位.RemoteParameters.RmtRec|=0;          //接收到0    }else if(RemoteParameters.Dval>1400&&RemoteParameters.Dval<1800) //1680为标准值,1680us{RemoteParameters.RmtRec<<=1;         //左移一位.RemoteParameters.RmtRec|=1;          //接收到1}else if(RemoteParameters.Dval>2200&&RemoteParameters.Dval<2600) //得到按键键值增加的信息 2500为标准值2.5ms{RemoteParameters.RmtCnt++;              //按键次数增加1次RemoteParameters.RmtSta&=0XF0;         //清空计时器  }}else if(RemoteParameters.Dval>4200&&RemoteParameters.Dval<4700)  //4500为标准值4.5ms{RemoteParameters.RmtSta|=1<<7;          //标记成功接收到了引导码RemoteParameters.RmtCnt=0;           //清除按键次数计数器}       }RemoteParameters.RmtSta&=~(1<<4);}}
}

   定时器4中断回调函数,主要用来计算定时器溢出次数。

//定时器中断回调函数中调用此函数,计算溢出次数
void RemoteDataTimerCallBack()
{if(RemoteParameters.RmtSta&0x80)                     //上次有数据被接收到了{ RemoteParameters.RmtSta&=~0X10;                     //取消上升沿已经被捕获标记if((RemoteParameters.RmtSta&0X0F)==0X00)RemoteParameters.RmtSta|=1<<6; //标记已经完成一次按键的键值信息采集if((RemoteParameters.RmtSta&0X0F)<14)RemoteParameters.RmtSta++;else{RemoteParameters.RmtSta&=~(1<<7);    //清空引导标识RemoteParameters.RmtSta&=0XF0;     //清空计数器 }            }
}
  • 遥控器实际键值数据校验

   上面两个函数就实现了对NEC编码的解码工作,接下来对数据进行处理,对地址码、数据原码、数据反码进行判断,是否有误码。

//处理红外键盘
//返回值:
//0,没有任何按键按下
//其他,按下的按键键值.
uint8_t Remote_Scan(void)
{        uint8_t sta=0;       uint8_t t1,t2;  if(RemoteParameters.RmtSta&(1<<6))         //得到一个按键的所有信息了{ t1=RemoteParameters.RmtRec>>24;         //得到地址码t2=(RemoteParameters.RmtRec>>16)&0xff;       //得到地址反码 if((t1==(uint8_t)~t2)&&t1==REMOTE_ID)       //检验遥控识别码(ID)及地址 { t1=RemoteParameters.RmtRec>>8;t2=RemoteParameters.RmtRec;  if(t1==(uint8_t)~t2)sta=t1;         //键值正确  RemoteParameters.RmtRec=0;}   if((sta==0)||((RemoteParameters.RmtSta&0X80)==0))    //按键数据错误/遥控已经没有按下了{RemoteParameters.RmtSta&=~(1<<6);       //清除接收到有效按键标识RemoteParameters.RmtCnt=0;         //清除按键次数计数器RemoteParameters.RmtRec=0;}}  return sta;
}
  • 遥控器标签键值与实际键值对应处理

  对于解码来说,得到的不过是遥控器发送来的数据,但是市面上的NEC编码遥控器遵从的只是协议一致,每个遥控器的按键对应的实际键值并不是一致的,比如A厂家的遥控器“按键1”对应的码值为100,B厂家的“按键1”可能是码值100,但也有可能是其他码值,此时就需要我们把遥控器标签上对应的键跟实际接收的键值进行匹配,下面是我接收到的我所购买的遥控器的实际键值,也就是图示遥控器标签对应的键值,实际测试跟正点原子的是不一样的,正点原子的键更多些,键值有他自己的定义。

   对程序设计者来说,实际键值有了,至于面向用户是什么意义,操作空间就非常大了,下面就我手里的遥控器,将码值跟标签值一一对应起来,代码如下,一顿操作猛如虎,就完成了我们键值的对应工作:

char *KeyValueConvert(uint8_t keyValue)
{char *str=0;switch (keyValue){case 162:str = "1";break;case 98:str = "2";break;case 226:str = "3";break; case 34:str = "4";break;case 2:str = "5";break;case 194:str = "6";break;case 224:str = "7";break;case 168:str = "8";break;case 144:str = "9";break;case 104:str = "*";break;case 152:str = "0";break;case 176:str = "#";break;case 24:str = "UP";break;case 16:str = "LEFT";break;case 56:str = "OK";break;case 90:str = "RIGHT";break;case 74:str = "DOWN";break;}//printf("The KeyValue is %s \r\n",(char *)(str));return str;
}

  来看下实际效果,我是采用万能的串口输出,跟我们遥控器的键值就完美的对应起来了,可能有心细的小伙伴已经看到了,下面方向键,串口还打印出了,旋转度数,是的,接下来我们来通过红外遥控器对舵机进行控制。

  • 红外遥控控制舵机转动固定角度

  我们是用上、下、左、右方向键和OK键分别对应舵机的0度、45度、90度、135度、180度5个角度,先来看一下4路PWM输出的代码,两个输入参数,第一个控制那一路输出,第二个参数设置旋转的角度0-180度:

/* USER CODE BEGIN 1 */
void PWM_Stop(void)
{HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_1);HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_3);HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);}
void PWM_Output(uint8_t PWM_Channel,int Angle)
{switch (PWM_Channel){case 1:switch(Angle){case 0://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);break;case 45://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,1000);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);break;case 90://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,1500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);break;case 135://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,2000);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);break;case 180://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);             __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,2500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);break;default:HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_1);}break;case 2:switch(Angle){case 0://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);break;case 45://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,1000);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);break;case 90://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,1500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);break;case 135://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,2000);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);break;case 180://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);             __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,2500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);break;default:HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_2);} break;case 3:switch(Angle){case 0://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);break;case 45://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,1000);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);break;case 90://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,1500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);break;case 135://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,2000);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);break;case 180://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);             __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,2500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);break;default:HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_3);} break;case 4:switch(Angle){case 0://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_4,500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);break;case 45://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_4,1000);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);break;case 90://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_4,1500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);break;case 135://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_4,2000);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);break;case 180://HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);             __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_4,2500);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);break;default:HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_4);} break;}
}

  然后我们是用红外遥控器来控制舵机转动不同的角度,实现代码如下:

//以下是红外控制舵机测试代码**********************************************/HW_KeyValue=Remote_Scan();if(HW_KeyValue){keystr = KeyValueConvert(HW_KeyValue);if(!memcmp(keystr,"*",strlen("*"))){PWM_Stop();printf("\r\nrotate stop\r\n");}else if(!memcmp(keystr,"UP",strlen("UP"))){PWM_Output(1,0);PWM_Output(2,0);PWM_Output(3,0);PWM_Output(4,0);printf("\r\nrotate 45度\r\n");}else if(!memcmp(keystr,"RIGHT",strlen("RIGHT"))){PWM_Output(1,45);PWM_Output(2,45);PWM_Output(3,45);PWM_Output(4,45);printf("\r\nrotate 90度\r\n"); } else if(!memcmp(keystr,"DOWN",strlen("DOWN"))){PWM_Output(1,90);PWM_Output(2,90);PWM_Output(3,90);PWM_Output(4,90);printf("\r\nrotate -45度\r\n");}else if(!memcmp(keystr,"LEFT",strlen("LEFT"))){PWM_Output(1,135);PWM_Output(2,135);PWM_Output(3,135);PWM_Output(4,135);printf("\r\nrotate -90度\r\n");}else if(!memcmp(keystr,"OK",strlen("OK"))){PWM_Output(1,180);PWM_Output(2,180);PWM_Output(3,180);PWM_Output(4,180);printf("\r\nrotate 90度\r\n");} printf("\r\nThe Key Value is %s \r\n",(char *)(keystr));}/**********************************************/

  由于只有一个舵机,只能先通过逻辑分析仪来测试输出波形,目前是设计的4路输出相同,小伙伴们可以根据自己的需要去设计具体的转动角度。

演示视频见公众号:

本次要分享的内容就要结束啦,希望对正在学习红外控制使用及舵机控制的童鞋有帮助,更多精彩内容,欢迎各位加群一起交流,获取本次红外控制舵机的源码,或者在公众号回复资料获取!

  如果你觉得对自己有帮助的话,给个赞,点个关注,点个在看,感谢前进的道路上有你的陪伴!图片欢迎大家关注公众号:嵌入式实验基地,让我快点遇到优秀的你,然后一起变得更加优秀,加油!!!

小飞哥微信号:w974762670,加好友进群一起交流呀!

红外通讯之红外遥控器让你的舵机转圈圈相关推荐

  1. 国网铅封电表红外通讯采集器是干什么用的?一分钟搞懂

    无线红外通讯采集器抄表终端是一款基于红外通讯,4G无线传输,直流宽电压供电的通用电表燃气表抄表终端,可采集南网 国网电表的电能数据.电压.电流.功率因数等电参数数据. 红外抄表原理简介:红外光电探头通 ...

  2. 【STM32】R05D电控红外协议的美的空调遥控器

    目录 一.设计思路 一.R05D红外协议原理 1.协议手册理解 2.验证时序(重点) 二.硬件实现 1.需要的材料 2.对发射模块电路进行修改 3.STM32 GPIO选择 三.代码实现 1.载波38 ...

  3. 红外通讯的信号调制及解调电路分析

    红外调制及解调电路讲解 红外通讯的信号调制及解调电路分析 信号解调 硬件 硬件接收电路 信号调制 红外NEC协议 加强记忆 谢谢观看! 红外通讯的信号调制及解调电路分析 前两天有个朋友咨询我激光信号调 ...

  4. ESP8266使用红外VS1838接收红外信号

    1.硬件部分         1.ESP80226NodeMcu一块         2.杜邦线若干(6根左右)         3.VS1838红外接收模块一个         4.数据线一根   ...

  5. stm32单片机+amg8833+红外热成像/单片机红外测温成像/stm32 amg8833红外热成像

    基于stm32单片机的amg8833红外热成像/单片机红外测温成像,测温模块用的是AMG8833 IR 8x8红外热像传感器. 具体功能:可红外热成像,可以设置报警阈值,可以语音播报温度异常,单片机s ...

  6. 【红外技术】客户的问题:在我的应用中,到底是选择中波红外还是长波红外波段?(来自英国高级技术咨询业界巨头的权威回答)

    整理时间:2020-02-16 整理内容:红外波段的选择 客户的问题:在我的应用中,到底是选择中波红外还是长波红外波段? 正式回答这个问题,还真是难. 因为我没有根据各种环境,气候等去做两个波段的对比 ...

  7. 近红外和短波红外的区别

    红外线是波长介乎微波与可见光之间的电磁波,波长为0.75-1000 μm,其中,近红外.短波红外.中波红外.长波红外所在区间如下: - 近红外 (Near Infrared, NIR) : 0.75~ ...

  8. Arduino 红外模块 红外接收 红外发射

    需要用到IRremote库文件 红外遥控按键16进制编码,使用时添加前缀 0X 红外接收 .源代码 //***************** //红外接收模块测试 //***************** ...

  9. 单片机红外通信c语言,用51单片机实现红外通讯源码

    /************************************************************************************** *            ...

最新文章

  1. computer vision(计算机视觉)方面的期刊会议,学术必备
  2. 人还是很需要成就感的
  3. 改变静态文本notify 属性_CocosCreator脚本属性个性化定制——下拉列表属性、滑动条属性...
  4. 端口如何支持非localhost访问_新特性解读 | MySQL 8.0.19 支持 DNS SRV
  5. vi通过Vundle安装和删除插件
  6. 基础编程题之查找组成一个偶数最接近的两个素数
  7. 【jeecg移动开发能力】表单移动开发能力,提供多套表单模板(移动端、PC端),支持自定义
  8. 类如何调用自己的私有成员_企业如何快速获取自己的私有领域流量?
  9. 服务器没权限修改,ftp服务器没有修改权限
  10. html5简单提问,【问答技巧】怎样提问才能让我的问题更快速被解答?
  11. 腾讯视频 Python 爬虫项目实战
  12. 免费下载百度文库 道客巴巴 豆丁 等付费文档
  13. matlab向量函数求梯度,用Matlab计算含有n个自变量的函数的梯度或句柄的使用
  14. win7计算机自动关机设置在哪里设置方法,win7电脑自动关机怎么设置_win7电脑自动关机怎么设置在哪-win7之家...
  15. emacs-打开和关闭
  16. Android系统优化的那些年那些事
  17. Cortex-A7中断系统
  18. ubuntu 破解Beyond compare 4
  19. 纯js写的手机版成语填空游戏
  20. 计算机专业对应的职业,这十个高薪职业对应哪些大学专业?看完你就明白啦

热门文章

  1. ubuntu上python编辑器_Ubuntu中安装python编辑器Ulipad
  2. 为了孩子学英语,花几天时间写了个语法练习小程序
  3. Vue.js从入门到精通(全),爆肝十一万字,“建议收藏”,国庆福利!!!
  4. java se系列(一)开发前奏
  5. uniapp 国际化
  6. 【项目实战】Python实现基于LDA主题模型进行电商产品评论数据情感分析
  7. 跟小米、特斯拉分“蛋糕”的优必选要IPO
  8. 获取字符串中最长单词的长度
  9. 阿里云将静态html网页部署至云服务器
  10. Acronis Backup  Recovery 10 key_七夕小子_新浪博客